tempest-react-sdk 0.39.0 → 0.40.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/dist/_virtual/_rolldown/runtime.cjs +1 -1
  2. package/dist/access/access-control-context.cjs.map +1 -1
  3. package/dist/access/use-can.cjs.map +1 -1
  4. package/dist/app/AppProviders.cjs.map +1 -1
  5. package/dist/app/AppProviders.js.map +1 -1
  6. package/dist/audio/use-audio-recorder.cjs.map +1 -1
  7. package/dist/audio/use-audio.cjs.map +1 -1
  8. package/dist/audio/use-microphone.cjs.map +1 -1
  9. package/dist/auth/create-auth-store.cjs.map +1 -1
  10. package/dist/auth/lazy-with-retry.cjs.map +1 -1
  11. package/dist/auth/passkey.cjs.map +1 -1
  12. package/dist/auth/passkey.js.map +1 -1
  13. package/dist/auth/use-passkey.cjs.map +1 -1
  14. package/dist/br/BrazilMap.cjs +1 -1
  15. package/dist/br/BrazilMap.cjs.map +1 -1
  16. package/dist/br/BrazilMap.js +2 -2
  17. package/dist/br/BrazilMap.js.map +1 -1
  18. package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
  19. package/dist/br/BrazilStateCitySelect.js.map +1 -1
  20. package/dist/br/BrazilStateMap.cjs +1 -1
  21. package/dist/br/BrazilStateMap.cjs.map +1 -1
  22. package/dist/br/BrazilStateMap.js +2 -2
  23. package/dist/br/BrazilStateMap.js.map +1 -1
  24. package/dist/br/MapLegend.cjs.map +1 -1
  25. package/dist/br/MapLegend.js.map +1 -1
  26. package/dist/br/MapMarkers.cjs.map +1 -1
  27. package/dist/br/MapMarkers.js.map +1 -1
  28. package/dist/br/MunicipalitySearch.cjs.map +1 -1
  29. package/dist/br/MunicipalitySearch.js.map +1 -1
  30. package/dist/br/PixQRCode.cjs.map +1 -1
  31. package/dist/br/PixQRCode.js.map +1 -1
  32. package/dist/br/use-map-hover.cjs.map +1 -1
  33. package/dist/br/use-map-zoom.cjs.map +1 -1
  34. package/dist/capture/media-recorder.cjs +1 -1
  35. package/dist/capture/media-recorder.js +2 -2
  36. package/dist/capture/use-barcode-scanner.cjs +1 -1
  37. package/dist/capture/use-barcode-scanner.cjs.map +1 -1
  38. package/dist/capture/use-barcode-scanner.js +2 -2
  39. package/dist/capture/use-barcode-scanner.js.map +1 -1
  40. package/dist/capture/use-screen-capture.cjs.map +1 -1
  41. package/dist/capture/use-speech-recognition.cjs.map +1 -1
  42. package/dist/capture/use-torch.cjs.map +1 -1
  43. package/dist/capture/use-video-recorder.cjs.map +1 -1
  44. package/dist/charts/AreaChart.cjs.map +1 -1
  45. package/dist/charts/AreaChart.js.map +1 -1
  46. package/dist/charts/BarChart.cjs.map +1 -1
  47. package/dist/charts/BarChart.js.map +1 -1
  48. package/dist/charts/LineChart.cjs.map +1 -1
  49. package/dist/charts/LineChart.js.map +1 -1
  50. package/dist/charts/PieChart.cjs.map +1 -1
  51. package/dist/charts/PieChart.js.map +1 -1
  52. package/dist/charts/RadarChart.cjs.map +1 -1
  53. package/dist/charts/RadarChart.js.map +1 -1
  54. package/dist/charts/use-chart-colors.cjs.map +1 -1
  55. package/dist/components/AIChat/AIChat.cjs.map +1 -1
  56. package/dist/components/AIChat/AIChat.js.map +1 -1
  57. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
  58. package/dist/components/AIChat/AIChatComposer.js.map +1 -1
  59. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
  60. package/dist/components/AIChat/AIChatTurn.js.map +1 -1
  61. package/dist/components/Accordion/Accordion.cjs.map +1 -1
  62. package/dist/components/Accordion/Accordion.js.map +1 -1
  63. package/dist/components/Alert/Alert.cjs.map +1 -1
  64. package/dist/components/Alert/Alert.js.map +1 -1
  65. package/dist/components/AppBar/AppBar.cjs.map +1 -1
  66. package/dist/components/AppBar/AppBar.js.map +1 -1
  67. package/dist/components/AppShell/AppShell.cjs.map +1 -1
  68. package/dist/components/AppShell/AppShell.js.map +1 -1
  69. package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
  70. package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
  71. package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
  72. package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
  73. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  74. package/dist/components/Avatar/Avatar.js.map +1 -1
  75. package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
  76. package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
  77. package/dist/components/Badge/Badge.cjs.map +1 -1
  78. package/dist/components/Badge/Badge.js.map +1 -1
  79. package/dist/components/Banner/Banner.cjs.map +1 -1
  80. package/dist/components/Banner/Banner.js.map +1 -1
  81. package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
  82. package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
  83. package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
  84. package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
  85. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  86. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  87. package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
  88. package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  89. package/dist/components/Button/Button.cjs.map +1 -1
  90. package/dist/components/Button/Button.js.map +1 -1
  91. package/dist/components/Calendar/Calendar.cjs +1 -1
  92. package/dist/components/Calendar/Calendar.cjs.map +1 -1
  93. package/dist/components/Calendar/Calendar.js +38 -41
  94. package/dist/components/Calendar/Calendar.js.map +1 -1
  95. package/dist/components/Card/Card.cjs.map +1 -1
  96. package/dist/components/Card/Card.js.map +1 -1
  97. package/dist/components/Carousel/Carousel.cjs.map +1 -1
  98. package/dist/components/Carousel/Carousel.js.map +1 -1
  99. package/dist/components/Chat/Chat.cjs.map +1 -1
  100. package/dist/components/Chat/Chat.js.map +1 -1
  101. package/dist/components/Chat/ChatComposer.cjs.map +1 -1
  102. package/dist/components/Chat/ChatComposer.js.map +1 -1
  103. package/dist/components/Chat/chat-groups.cjs +1 -1
  104. package/dist/components/Chat/chat-groups.cjs.map +1 -1
  105. package/dist/components/Chat/chat-groups.js +2 -2
  106. package/dist/components/Chat/chat-groups.js.map +1 -1
  107. package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
  108. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  109. package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
  110. package/dist/components/ChipInput/ChipInput.js.map +1 -1
  111. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  112. package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
  113. package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
  114. package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
  115. package/dist/components/Collapsible/Collapsible.js.map +1 -1
  116. package/dist/components/Combobox/Combobox.cjs.map +1 -1
  117. package/dist/components/Combobox/Combobox.js.map +1 -1
  118. package/dist/components/Command/Command.cjs.map +1 -1
  119. package/dist/components/Command/Command.js.map +1 -1
  120. package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
  121. package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
  122. package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
  123. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  124. package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
  125. package/dist/components/CopyButton/CopyButton.js.map +1 -1
  126. package/dist/components/DataList/DataList.cjs.map +1 -1
  127. package/dist/components/DataList/DataList.js.map +1 -1
  128. package/dist/components/DataTable/DataTable.cjs +1 -1
  129. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  130. package/dist/components/DataTable/DataTable.js +2 -2
  131. package/dist/components/DataTable/DataTable.js.map +1 -1
  132. package/dist/components/DataTable/EditableCell.cjs.map +1 -1
  133. package/dist/components/DataTable/EditableCell.js.map +1 -1
  134. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  135. package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
  136. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  137. package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
  138. package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
  139. package/dist/components/Divider/Divider.cjs.map +1 -1
  140. package/dist/components/Divider/Divider.js.map +1 -1
  141. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  142. package/dist/components/Drawer/Drawer.js.map +1 -1
  143. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  144. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  145. package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
  146. package/dist/components/Dropzone/Dropzone.js.map +1 -1
  147. package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
  148. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  149. package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
  150. package/dist/components/ErrorState/ErrorState.js.map +1 -1
  151. package/dist/components/FileUpload/FileUpload.cjs +1 -1
  152. package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
  153. package/dist/components/FileUpload/FileUpload.js +1 -1
  154. package/dist/components/FileUpload/FileUpload.js.map +1 -1
  155. package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
  156. package/dist/components/FilterBar/FilterBar.js.map +1 -1
  157. package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
  158. package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
  159. package/dist/components/Form/Form.cjs.map +1 -1
  160. package/dist/components/Form/Form.js.map +1 -1
  161. package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
  162. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  163. package/dist/components/Image/Image.cjs.map +1 -1
  164. package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
  165. package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
  166. package/dist/components/ImageCropper/ImageCropper.js +1 -1
  167. package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
  168. package/dist/components/Input/Input.cjs.map +1 -1
  169. package/dist/components/Input/Input.js.map +1 -1
  170. package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
  171. package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
  172. package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
  173. package/dist/components/InstallButton/InstallButton.js.map +1 -1
  174. package/dist/components/Kanban/Kanban.cjs.map +1 -1
  175. package/dist/components/Kanban/Kanban.js.map +1 -1
  176. package/dist/components/Label/Label.cjs.map +1 -1
  177. package/dist/components/Label/Label.js.map +1 -1
  178. package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
  179. package/dist/components/Lightbox/Lightbox.js.map +1 -1
  180. package/dist/components/ListTile/ListTile.cjs.map +1 -1
  181. package/dist/components/ListTile/ListTile.js.map +1 -1
  182. package/dist/components/Markdown/Markdown.cjs +1 -1
  183. package/dist/components/Markdown/Markdown.cjs.map +1 -1
  184. package/dist/components/Markdown/Markdown.js +2 -2
  185. package/dist/components/Markdown/Markdown.js.map +1 -1
  186. package/dist/components/Masonry/Masonry.cjs.map +1 -1
  187. package/dist/components/Masonry/Masonry.js.map +1 -1
  188. package/dist/components/Menubar/Menubar.cjs.map +1 -1
  189. package/dist/components/Menubar/Menubar.js.map +1 -1
  190. package/dist/components/Modal/Modal.cjs.map +1 -1
  191. package/dist/components/Modal/Modal.js.map +1 -1
  192. package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
  193. package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
  194. package/dist/components/Money/Money.cjs.map +1 -1
  195. package/dist/components/Money/Money.js.map +1 -1
  196. package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
  197. package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
  198. package/dist/components/NProgress/NProgress.cjs.map +1 -1
  199. package/dist/components/Navbar/Navbar.cjs.map +1 -1
  200. package/dist/components/Navbar/Navbar.js.map +1 -1
  201. package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
  202. package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
  203. package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
  204. package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
  205. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
  206. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
  207. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
  208. package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
  209. package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
  210. package/dist/components/Page/Page.cjs.map +1 -1
  211. package/dist/components/Page/Page.js.map +1 -1
  212. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  213. package/dist/components/Pagination/Pagination.js.map +1 -1
  214. package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
  215. package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
  216. package/dist/components/PinInput/PinInput.cjs.map +1 -1
  217. package/dist/components/PinInput/PinInput.js.map +1 -1
  218. package/dist/components/Popover/Popover.cjs.map +1 -1
  219. package/dist/components/Popover/Popover.js.map +1 -1
  220. package/dist/components/Portal/Portal.cjs.map +1 -1
  221. package/dist/components/Progress/Progress.cjs.map +1 -1
  222. package/dist/components/Progress/Progress.js.map +1 -1
  223. package/dist/components/QRCode/QRCode.cjs.map +1 -1
  224. package/dist/components/QRCode/QRCode.js.map +1 -1
  225. package/dist/components/Radio/Radio.cjs.map +1 -1
  226. package/dist/components/Radio/Radio.js.map +1 -1
  227. package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
  228. package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
  229. package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
  230. package/dist/components/RatingStars/RatingStars.js.map +1 -1
  231. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  232. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  233. package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
  234. package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
  235. package/dist/components/Resizable/Resizable.cjs.map +1 -1
  236. package/dist/components/Resizable/Resizable.js.map +1 -1
  237. package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
  238. package/dist/components/Scheduler/Scheduler.js.map +1 -1
  239. package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
  240. package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
  241. package/dist/components/SearchBar/SearchBar.js.map +1 -1
  242. package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
  243. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
  244. package/dist/components/Select/Select.cjs.map +1 -1
  245. package/dist/components/Select/Select.js.map +1 -1
  246. package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
  247. package/dist/components/Sidebar/Sidebar.js.map +1 -1
  248. package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
  249. package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
  250. package/dist/components/Slider/Slider.cjs.map +1 -1
  251. package/dist/components/Slider/Slider.js.map +1 -1
  252. package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
  253. package/dist/components/Sparkline/Sparkline.js.map +1 -1
  254. package/dist/components/Spinner/Spinner.cjs.map +1 -1
  255. package/dist/components/Spinner/Spinner.js.map +1 -1
  256. package/dist/components/Stat/Stat.cjs.map +1 -1
  257. package/dist/components/Stat/Stat.js.map +1 -1
  258. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  259. package/dist/components/Stepper/Stepper.js.map +1 -1
  260. package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
  261. package/dist/components/StepperInput/StepperInput.js.map +1 -1
  262. package/dist/components/Switch/Switch.cjs.map +1 -1
  263. package/dist/components/Switch/Switch.js.map +1 -1
  264. package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
  265. package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
  266. package/dist/components/Table/Table.cjs.map +1 -1
  267. package/dist/components/Table/Table.js.map +1 -1
  268. package/dist/components/Tabs/Tabs.cjs.map +1 -1
  269. package/dist/components/Tabs/Tabs.js.map +1 -1
  270. package/dist/components/Tag/Tag.cjs.map +1 -1
  271. package/dist/components/Tag/Tag.js.map +1 -1
  272. package/dist/components/Textarea/Textarea.cjs.map +1 -1
  273. package/dist/components/Textarea/Textarea.js.map +1 -1
  274. package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
  275. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  276. package/dist/components/Timeline/Timeline.cjs.map +1 -1
  277. package/dist/components/Timeline/Timeline.js.map +1 -1
  278. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  279. package/dist/components/Toast/ToastProvider.js.map +1 -1
  280. package/dist/components/Toggle/Toggle.cjs.map +1 -1
  281. package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
  282. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
  283. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  284. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  285. package/dist/components/Tour/Tour.cjs.map +1 -1
  286. package/dist/components/Tour/Tour.js.map +1 -1
  287. package/dist/components/Transfer/Transfer.cjs.map +1 -1
  288. package/dist/components/Transfer/Transfer.js.map +1 -1
  289. package/dist/components/TreeView/TreeView.cjs +1 -1
  290. package/dist/components/TreeView/TreeView.cjs.map +1 -1
  291. package/dist/components/TreeView/TreeView.js +1 -4
  292. package/dist/components/TreeView/TreeView.js.map +1 -1
  293. package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
  294. package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
  295. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  296. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  297. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
  298. package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
  299. package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
  300. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  301. package/dist/components/Wizard/Wizard.js.map +1 -1
  302. package/dist/data/data-provider-context.cjs.map +1 -1
  303. package/dist/data/use-resource.cjs.map +1 -1
  304. package/dist/editor/RichTextEditor.cjs.map +1 -1
  305. package/dist/editor/RichTextEditor.js.map +1 -1
  306. package/dist/error-boundary/use-error-handler.cjs.map +1 -1
  307. package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
  308. package/dist/forms/FormField.cjs.map +1 -1
  309. package/dist/forms/masked-inputs.cjs +1 -1
  310. package/dist/forms/masked-inputs.cjs.map +1 -1
  311. package/dist/forms/masked-inputs.js +1 -1
  312. package/dist/forms/masked-inputs.js.map +1 -1
  313. package/dist/forms/use-viacep.cjs.map +1 -1
  314. package/dist/forms/use-zod-form.cjs.map +1 -1
  315. package/dist/forms/zod-resolver.cjs.map +1 -1
  316. package/dist/forms/zod-resolver.js.map +1 -1
  317. package/dist/geo/TrajectoryMap.cjs.map +1 -1
  318. package/dist/geo/TrajectoryMap.js.map +1 -1
  319. package/dist/geo/leaflet-map.cjs.map +1 -1
  320. package/dist/geo/leaflet-map.js.map +1 -1
  321. package/dist/geo/use-position-tracker.cjs.map +1 -1
  322. package/dist/hooks/use-announce.cjs.map +1 -1
  323. package/dist/hooks/use-async.cjs.map +1 -1
  324. package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
  325. package/dist/hooks/use-breakpoint.cjs.map +1 -1
  326. package/dist/hooks/use-click-outside.cjs.map +1 -1
  327. package/dist/hooks/use-client-filter.cjs.map +1 -1
  328. package/dist/hooks/use-clipboard.cjs.map +1 -1
  329. package/dist/hooks/use-counter.cjs.map +1 -1
  330. package/dist/hooks/use-debounce.cjs.map +1 -1
  331. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  332. package/dist/hooks/use-disclosure.cjs.map +1 -1
  333. package/dist/hooks/use-document-visibility.cjs.map +1 -1
  334. package/dist/hooks/use-event-listener.cjs.map +1 -1
  335. package/dist/hooks/use-geolocation.cjs.map +1 -1
  336. package/dist/hooks/use-hover.cjs.map +1 -1
  337. package/dist/hooks/use-idle.cjs.map +1 -1
  338. package/dist/hooks/use-install-prompt.cjs +1 -1
  339. package/dist/hooks/use-install-prompt.cjs.map +1 -1
  340. package/dist/hooks/use-install-prompt.js +1 -1
  341. package/dist/hooks/use-install-prompt.js.map +1 -1
  342. package/dist/hooks/use-intersection-observer.cjs.map +1 -1
  343. package/dist/hooks/use-interval.cjs.map +1 -1
  344. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  345. package/dist/hooks/use-keyboard-shortcut.cjs +1 -1
  346. package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
  347. package/dist/hooks/use-keyboard-shortcut.js +1 -1
  348. package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
  349. package/dist/hooks/use-list-state.cjs.map +1 -1
  350. package/dist/hooks/use-local-storage.cjs.map +1 -1
  351. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  352. package/dist/hooks/use-long-press.cjs.map +1 -1
  353. package/dist/hooks/use-map.cjs.map +1 -1
  354. package/dist/hooks/use-media-devices.cjs.map +1 -1
  355. package/dist/hooks/use-media-permission.cjs.map +1 -1
  356. package/dist/hooks/use-media-query.cjs.map +1 -1
  357. package/dist/hooks/use-object-url.cjs.map +1 -1
  358. package/dist/hooks/use-online.cjs.map +1 -1
  359. package/dist/hooks/use-pagination.cjs.map +1 -1
  360. package/dist/hooks/use-previous.cjs.map +1 -1
  361. package/dist/hooks/use-queue.cjs.map +1 -1
  362. package/dist/hooks/use-resize-observer.cjs.map +1 -1
  363. package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
  364. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  365. package/dist/hooks/use-set.cjs.map +1 -1
  366. package/dist/hooks/use-sortable.cjs +1 -1
  367. package/dist/hooks/use-sortable.cjs.map +1 -1
  368. package/dist/hooks/use-sortable.js +0 -2
  369. package/dist/hooks/use-sortable.js.map +1 -1
  370. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  371. package/dist/hooks/use-storage-estimate.cjs.map +1 -1
  372. package/dist/hooks/use-throttle.cjs.map +1 -1
  373. package/dist/hooks/use-timeout.cjs.map +1 -1
  374. package/dist/hooks/use-toggle.cjs.map +1 -1
  375. package/dist/hooks/use-window-size.cjs.map +1 -1
  376. package/dist/http/api-client.cjs +1 -1
  377. package/dist/http/api-client.cjs.map +1 -1
  378. package/dist/http/api-client.js +7 -5
  379. package/dist/http/api-client.js.map +1 -1
  380. package/dist/http/resumable-upload.cjs +1 -1
  381. package/dist/http/resumable-upload.cjs.map +1 -1
  382. package/dist/http/resumable-upload.js +1 -1
  383. package/dist/http/resumable-upload.js.map +1 -1
  384. package/dist/http/use-poll.cjs.map +1 -1
  385. package/dist/i18n/I18nProvider.cjs.map +1 -1
  386. package/dist/icons/Icon.cjs.map +1 -1
  387. package/dist/icons/IconProvider.cjs.map +1 -1
  388. package/dist/icons/icon-context.cjs.map +1 -1
  389. package/dist/icons/use-icon.cjs.map +1 -1
  390. package/dist/imaging/use-image-processing.cjs.map +1 -1
  391. package/dist/oauth/GoogleSignIn.cjs.map +1 -1
  392. package/dist/oauth/GoogleSignIn.js.map +1 -1
  393. package/dist/oauth/use-oauth-callback.cjs.map +1 -1
  394. package/dist/offline/use-offline-sync.cjs.map +1 -1
  395. package/dist/push/use-push-subscription.cjs.map +1 -1
  396. package/dist/query/QueryProvider.cjs.map +1 -1
  397. package/dist/query/constants.cjs +1 -1
  398. package/dist/query/constants.cjs.map +1 -1
  399. package/dist/query/constants.js +10 -10
  400. package/dist/query/constants.js.map +1 -1
  401. package/dist/query/create-query-keys.cjs +1 -1
  402. package/dist/query/create-query-keys.cjs.map +1 -1
  403. package/dist/query/create-query-keys.js +1 -1
  404. package/dist/query/create-query-keys.js.map +1 -1
  405. package/dist/query/offline-persistence.cjs.map +1 -1
  406. package/dist/query/use-cursor-query.cjs.map +1 -1
  407. package/dist/query/use-offline-mutation.cjs.map +1 -1
  408. package/dist/query/use-paginated-query.cjs.map +1 -1
  409. package/dist/router/AppRouter.cjs +1 -1
  410. package/dist/router/AppRouter.cjs.map +1 -1
  411. package/dist/router/AppRouter.js +7 -2
  412. package/dist/router/AppRouter.js.map +1 -1
  413. package/dist/sse/use-event-stream.cjs.map +1 -1
  414. package/dist/store/create-store.cjs.map +1 -1
  415. package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
  416. package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
  417. package/dist/tempest-react-sdk.d.ts +25 -26
  418. package/dist/theme/ThemeProvider.cjs.map +1 -1
  419. package/dist/theme/color.cjs +1 -1
  420. package/dist/theme/color.cjs.map +1 -1
  421. package/dist/theme/color.js +1 -1
  422. package/dist/theme/color.js.map +1 -1
  423. package/dist/utils/cn.cjs +1 -1
  424. package/dist/utils/cn.cjs.map +1 -1
  425. package/dist/utils/cn.js +1 -1
  426. package/dist/utils/cn.js.map +1 -1
  427. package/dist/utils/objects.cjs +1 -1
  428. package/dist/utils/objects.cjs.map +1 -1
  429. package/dist/utils/objects.js +1 -1
  430. package/dist/utils/objects.js.map +1 -1
  431. package/dist/utils/xlsx.cjs +1 -1
  432. package/dist/utils/xlsx.cjs.map +1 -1
  433. package/dist/utils/xlsx.js +3 -3
  434. package/dist/utils/xlsx.js.map +1 -1
  435. package/dist/vision/core/exceptions.cjs.map +1 -1
  436. package/dist/vision/core/exceptions.js.map +1 -1
  437. package/dist/vision/core/metadata.cjs +1 -1
  438. package/dist/vision/core/metadata.cjs.map +1 -1
  439. package/dist/vision/core/metadata.js +1 -1
  440. package/dist/vision/core/metadata.js.map +1 -1
  441. package/dist/vision/io/image.cjs +1 -1
  442. package/dist/vision/io/image.cjs.map +1 -1
  443. package/dist/vision/io/image.js +4 -2
  444. package/dist/vision/io/image.js.map +1 -1
  445. package/dist/vision/preprocess/image.cjs +1 -1
  446. package/dist/vision/preprocess/image.cjs.map +1 -1
  447. package/dist/vision/preprocess/image.js +4 -2
  448. package/dist/vision/preprocess/image.js.map +1 -1
  449. package/dist/vision/tasks/base.cjs +1 -1
  450. package/dist/vision/tasks/base.cjs.map +1 -1
  451. package/dist/vision/tasks/base.js +3 -1
  452. package/dist/vision/tasks/base.js.map +1 -1
  453. package/dist/vision/tasks/classifier.cjs +1 -1
  454. package/dist/vision/tasks/classifier.cjs.map +1 -1
  455. package/dist/vision/tasks/classifier.js +2 -2
  456. package/dist/vision/tasks/classifier.js.map +1 -1
  457. package/dist/vision/tasks/detectClassify.cjs +1 -1
  458. package/dist/vision/tasks/detectClassify.cjs.map +1 -1
  459. package/dist/vision/tasks/detectClassify.js +16 -15
  460. package/dist/vision/tasks/detectClassify.js.map +1 -1
  461. package/dist/vision/use-camera-stream.cjs.map +1 -1
  462. package/dist/vision/use-live-luminance.cjs.map +1 -1
  463. package/dist/vite/create-vite-config.cjs +1 -1
  464. package/dist/vite/create-vite-config.cjs.map +1 -1
  465. package/dist/vite/create-vite-config.js +3 -3
  466. package/dist/vite/create-vite-config.js.map +1 -1
  467. package/dist/vite/tempest-icons.cjs.map +1 -1
  468. package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
  469. package/dist/vite/tempest-pwa-icons.cjs.map +1 -1
  470. package/dist/ws/use-web-socket.cjs +1 -1
  471. package/dist/ws/use-web-socket.cjs.map +1 -1
  472. package/dist/ws/use-web-socket.js +5 -3
  473. package/dist/ws/use-web-socket.js.map +1 -1
  474. package/package.json +2 -2
  475. package/template/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ListTile.js","names":[],"sources":["../../../src/components/ListTile/ListTile.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — leading, title, subtitle and trailing are the four\n * slots of a list row, and onClick/disabled/selected are what make it a row you can\n * pick. That is the whole component — there is no second one inside it.\n */\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./ListTile.module.css\";\n\nexport interface ListTileProps {\n /** Left slot — typically an icon or avatar. */\n leading?: ReactNode;\n /** Primary line. Required. */\n title: ReactNode;\n /** Secondary line rendered under the title. */\n subtitle?: ReactNode;\n /** Right slot — typically an icon, switch or meta text. */\n trailing?: ReactNode;\n /** When provided, the tile becomes an interactive button. */\n onClick?: () => void;\n /** When true, the tile is dimmed and non-interactive. */\n disabled?: boolean;\n /** When true, the tile is highlighted as the active row. */\n selected?: boolean;\n /** Extra class names merged onto the root. */\n className?: string;\n}\n\n/**\n * The canonical Material list row: an optional leading slot, a title with an\n * optional subtitle, and an optional trailing slot. When `onClick` is supplied\n * the tile renders as a full-width, keyboard-accessible `<button>`; otherwise it\n * renders as a static `<div>`.\n *\n * @example\n * <ListTile\n * leading={<User />}\n * title=\"Maria Silva\"\n * subtitle=\"maria@example.com\"\n * trailing={<ChevronRight />}\n * onClick={() => open(user)}\n * />\n */\nexport function ListTile({\n leading,\n title,\n subtitle,\n trailing,\n onClick,\n disabled = false,\n selected = false,\n className,\n}: ListTileProps) {\n const content = (\n <>\n {leading !== undefined && <span className={styles.leading}>{leading}</span>}\n <span className={styles.body}>\n <span className={styles.title}>{title}</span>\n {subtitle !== undefined && <span className={styles.subtitle}>{subtitle}</span>}\n </span>\n {trailing !== undefined && <span className={styles.trailing}>{trailing}</span>}\n </>\n );\n\n const rootClassName = cn(\n styles.tile,\n selected && styles.selected,\n disabled && styles.disabled,\n className,\n );\n\n if (onClick) {\n return (\n <button\n type=\"button\"\n className={cn(rootClassName, styles.interactive)}\n aria-pressed={selected ? true : undefined}\n disabled={disabled}\n onClick={onClick}\n >\n {content}\n </button>\n );\n }\n\n return <div className={rootClassName}>{content}</div>;\n}\n"],"mappings":";;;;AA2CA,SAAgB,EAAS,EACrB,YACA,UACA,aACA,aACA,YACA,cAAW,IACX,cAAW,IACX,gBACc;CACd,IAAM,IACF,kBAAA,GAAA,EAAA,UAAA;EACK,MAAY,KAAA,KAAa,kBAAC,QAAD;GAAM,WAAW,EAAO;aAAU;EAAc,CAAA;EAC1E,kBAAC,QAAD;GAAM,WAAW,EAAO;aAAxB,CACI,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAQ;GAAY,CAAA,GAC3C,MAAa,KAAA,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;cAAW;GAAe,CAAA,CAC3E;;EACL,MAAa,KAAA,KAAa,kBAAC,QAAD;GAAM,WAAW,EAAO;aAAW;EAAe,CAAA;CAC/E,EAAA,CAAA,GAGA,IAAgB,EAClB,EAAO,MACP,KAAY,EAAO,UACnB,KAAY,EAAO,UACnB,CACJ;CAgBA,OAdI,IAEI,kBAAC,UAAD;EACI,MAAK;EACL,WAAW,EAAG,GAAe,EAAO,WAAW;EAC/C,gBAAc,IAAW,KAAO,KAAA;EACtB;EACD;YAER;CACG,CAAA,IAIT,kBAAC,OAAD;EAAK,WAAW;YAAgB;CAAa,CAAA;AACxD"}
1
+ {"version":3,"file":"ListTile.js","names":[],"sources":["../../../src/components/ListTile/ListTile.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — leading, title, subtitle and trailing are the four\n * slots of a list row, and onClick/disabled/selected are what make it a row you can\n * pick. That is the whole component — there is no second one inside it.\n */\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./ListTile.module.css\";\n\nexport interface ListTileProps {\n /** Left slot — typically an icon or avatar. */\n leading?: ReactNode;\n /** Primary line. Required. */\n title: ReactNode;\n /** Secondary line rendered under the title. */\n subtitle?: ReactNode;\n /** Right slot — typically an icon, switch or meta text. */\n trailing?: ReactNode;\n /** When provided, the tile becomes an interactive button. */\n onClick?: () => void;\n /** When true, the tile is dimmed and non-interactive. */\n disabled?: boolean;\n /** When true, the tile is highlighted as the active row. */\n selected?: boolean;\n /** Extra class names merged onto the root. */\n className?: string;\n}\n\n/**\n * The canonical Material list row: an optional leading slot, a title with an\n * optional subtitle, and an optional trailing slot. When `onClick` is supplied\n * the tile renders as a full-width, keyboard-accessible `<button>`; otherwise it\n * renders as a static `<div>`.\n *\n * @example\n * <ListTile\n * leading={<User />}\n * title=\"Maria Silva\"\n * subtitle=\"maria@example.com\"\n * trailing={<ChevronRight />}\n * onClick={() => open(user)}\n * />\n */\nexport function ListTile({\n leading,\n title,\n subtitle,\n trailing,\n onClick,\n disabled = false,\n selected = false,\n className,\n}: ListTileProps) {\n const content = (\n <>\n {leading !== undefined && <span className={styles.leading}>{leading}</span>}\n <span className={styles.body}>\n <span className={styles.title}>{title}</span>\n {subtitle !== undefined && <span className={styles.subtitle}>{subtitle}</span>}\n </span>\n {trailing !== undefined && <span className={styles.trailing}>{trailing}</span>}\n </>\n );\n\n const rootClassName = cn(\n styles.tile,\n selected && styles.selected,\n disabled && styles.disabled,\n className,\n );\n\n if (onClick) {\n return (\n <button\n type=\"button\"\n className={cn(rootClassName, styles.interactive)}\n aria-pressed={selected ? true : undefined}\n disabled={disabled}\n onClick={onClick}\n >\n {content}\n </button>\n );\n }\n\n return <div className={rootClassName}>{content}</div>;\n}\n"],"mappings":";;;;AA2CA,SAAgB,EAAS,EACrB,YACA,UACA,aACA,aACA,YACA,cAAW,IACX,cAAW,IACX,gBACc;CACd,IAAM,IACF,kBAAA,GAAA,EAAA,UAAA;EACK,MAAY,KAAA,KAAa,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAU,UAAA;EAAc,CAAA;EAC1E,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAxB,UAAA,CACI,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAQ,UAAA;GAAY,CAAA,GAC3C,MAAa,KAAA,KAAa,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAW,UAAA;GAAe,CAAA,CAC3E;;EACL,MAAa,KAAA,KAAa,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAW,UAAA;EAAe,CAAA;CAC/E,EAAA,CAAA,GAGA,IAAgB,EAClB,EAAO,MACP,KAAY,EAAO,UACnB,KAAY,EAAO,UACnB,CACJ;CAgBA,OAdI,IAEI,kBAAC,UAAD;EACI,MAAK;EACL,WAAW,EAAG,GAAe,EAAO,WAAW;EAC/C,gBAAc,IAAW,KAAO,KAAA;EACtB;EACD;EAER,UAAA;CACG,CAAA,IAIT,kBAAC,OAAD;EAAK,WAAW;EAAgB,UAAA;CAAa,CAAA;AACxD"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-scroll-overflow.cjs"),n=require("../CodeBlock/CodeBlock.cjs"),r=require("./markdown-parse.cjs"),i=require("./Markdown.module.cjs");let a=require("react"),o=require("react/jsx-runtime");function s(e){return e.map((e,t)=>{let n=t;switch(e.type){case`text`:return(0,o.jsx)(a.Fragment,{children:e.value},n);case`strong`:return(0,o.jsx)(`strong`,{children:s(e.children)},n);case`em`:return(0,o.jsx)(`em`,{children:s(e.children)},n);case`del`:return(0,o.jsx)(`del`,{children:s(e.children)},n);case`code`:return(0,o.jsx)(`code`,{className:i.default.inlineCode,children:e.value},n);case`image`:return(0,o.jsx)(`img`,{className:i.default.image,src:e.src,alt:e.alt},n);case`break`:return(0,o.jsx)(`br`,{},n);default:return null}})}function c(e){return e?{textAlign:e}:void 0}function l({source:t,headingOffset:n=2,highlightCode:s=!0,showLineNumbers:c=!1,linkProps:l,className:d,...f}){let p=(0,a.useMemo)(()=>r.parseMarkdown(t),[t]);return(0,o.jsx)(`div`,{className:e.cn(i.default.root,d),...f,children:p.map((e,t)=>(0,o.jsx)(u,{block:e,headingOffset:n,highlightCode:s,showLineNumbers:c,linkProps:l},t))})}function u({block:e,headingOffset:t,highlightCode:r,showLineNumbers:c,linkProps:l}){let f=e=>e.map((e,n)=>(0,o.jsx)(u,{block:e,headingOffset:t,highlightCode:r,showLineNumbers:c,linkProps:l},n)),p=e=>e.map((e,t)=>e.type===`link`?(0,o.jsx)(`a`,{className:i.default.link,href:e.href,...l,children:s(e.children)},t):(0,o.jsx)(a.Fragment,{children:s([e])},t));switch(e.type){case`heading`:{let n=Math.min(6,Math.max(1,e.level+t-1));return(0,o.jsx)(`h${n}`,{className:i.default[`h${n}`],children:p(e.children)})}case`paragraph`:return(0,o.jsx)(`p`,{children:p(e.children)});case`code`:return r?(0,o.jsx)(n.CodeBlock,{code:e.value,language:e.language??void 0,showLineNumbers:c}):(0,o.jsx)(`pre`,{children:(0,o.jsx)(`code`,{children:e.value})});case`quote`:return(0,o.jsx)(`blockquote`,{className:i.default.quote,children:f(e.children)});case`rule`:return(0,o.jsx)(`hr`,{className:i.default.rule});case`list`:return e.ordered?(0,o.jsx)(`ol`,{className:i.default.list,start:e.start===1?void 0:e.start,children:e.items.map((e,t)=>(0,o.jsx)(`li`,{className:i.default.item,children:f(e)},t))}):(0,o.jsx)(`ul`,{className:i.default.list,children:e.items.map((e,t)=>(0,o.jsx)(`li`,{className:i.default.item,children:f(e)},t))});case`table`:return(0,o.jsx)(d,{block:e,inline:p});default:return null}}function d({block:e,inline:n}){let r=(0,a.useRef)(null),s=t.useScrollOverflow(r,`horizontal`);return(0,o.jsx)(`div`,{ref:r,className:i.default.tableScroll,tabIndex:s?0:void 0,role:s?`region`:void 0,"aria-label":s?`Tabela rolável`:void 0,children:(0,o.jsxs)(`table`,{className:i.default.table,children:[(0,o.jsx)(`thead`,{children:(0,o.jsx)(`tr`,{children:e.head.map((t,r)=>(0,o.jsx)(`th`,{style:c(e.align[r]??null),children:n(t)},r))})}),(0,o.jsx)(`tbody`,{children:e.rows.map((t,r)=>(0,o.jsx)(`tr`,{children:t.map((t,r)=>(0,o.jsx)(`td`,{style:c(e.align[r]??null),children:n(t)},r))},r))})]})})}exports.Markdown=l;
1
+ const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-scroll-overflow.cjs"),n=require("../CodeBlock/CodeBlock.cjs"),r=require("./markdown-parse.cjs"),i=require("./Markdown.module.cjs");let a=require("react"),o=require("react/jsx-runtime");function s(e){return e.map((e,t)=>{let n=t;switch(e.type){case`text`:return(0,o.jsx)(a.Fragment,{children:e.value},n);case`strong`:return(0,o.jsx)(`strong`,{children:s(e.children)},n);case`em`:return(0,o.jsx)(`em`,{children:s(e.children)},n);case`del`:return(0,o.jsx)(`del`,{children:s(e.children)},n);case`code`:return(0,o.jsx)(`code`,{className:i.default.inlineCode,children:e.value},n);case`image`:return(0,o.jsx)(`img`,{className:i.default.image,src:e.src,alt:e.alt},n);case`break`:return(0,o.jsx)(`br`,{},n);default:return null}})}function c(e){return e?{textAlign:e}:void 0}function l({source:t,headingOffset:n=2,highlightCode:s=!0,showLineNumbers:c=!1,linkProps:l,className:d,...f}){let p=(0,a.useMemo)(()=>r.parseMarkdown(t),[t]);return(0,o.jsx)(`div`,{className:e.cn(i.default.root,d),...f,children:p.map((e,t)=>(0,o.jsx)(u,{block:e,headingOffset:n,highlightCode:s,showLineNumbers:c,linkProps:l},t))})}function u({block:e,headingOffset:t,highlightCode:r,showLineNumbers:c,linkProps:l}){let f=e=>e.map((e,n)=>(0,o.jsx)(u,{block:e,headingOffset:t,highlightCode:r,showLineNumbers:c,linkProps:l},n)),p=e=>e.map((e,t)=>e.type===`link`?(0,o.jsx)(`a`,{className:i.default.link,href:e.href,...l,children:s(e.children)},t):(0,o.jsx)(a.Fragment,{children:s([e])},t));switch(e.type){case`heading`:{let n=Math.min(6,Math.max(1,e.level+t-1)),r=`h${n}`;return(0,o.jsx)(r,{className:i.default[`h${n}`],children:p(e.children)})}case`paragraph`:return(0,o.jsx)(`p`,{children:p(e.children)});case`code`:return r?(0,o.jsx)(n.CodeBlock,{code:e.value,language:e.language??void 0,showLineNumbers:c}):(0,o.jsx)(`pre`,{children:(0,o.jsx)(`code`,{children:e.value})});case`quote`:return(0,o.jsx)(`blockquote`,{className:i.default.quote,children:f(e.children)});case`rule`:return(0,o.jsx)(`hr`,{className:i.default.rule});case`list`:return e.ordered?(0,o.jsx)(`ol`,{className:i.default.list,start:e.start===1?void 0:e.start,children:e.items.map((e,t)=>(0,o.jsx)(`li`,{className:i.default.item,children:f(e)},t))}):(0,o.jsx)(`ul`,{className:i.default.list,children:e.items.map((e,t)=>(0,o.jsx)(`li`,{className:i.default.item,children:f(e)},t))});case`table`:return(0,o.jsx)(d,{block:e,inline:p});default:return null}}function d({block:e,inline:n}){let r=(0,a.useRef)(null),s=t.useScrollOverflow(r,`horizontal`);return(0,o.jsx)(`div`,{ref:r,className:i.default.tableScroll,tabIndex:s?0:void 0,role:s?`region`:void 0,"aria-label":s?`Tabela rolável`:void 0,children:(0,o.jsxs)(`table`,{className:i.default.table,children:[(0,o.jsx)(`thead`,{children:(0,o.jsx)(`tr`,{children:e.head.map((t,r)=>(0,o.jsx)(`th`,{style:c(e.align[r]??null),children:n(t)},r))})}),(0,o.jsx)(`tbody`,{children:e.rows.map((t,r)=>(0,o.jsx)(`tr`,{children:t.map((t,r)=>(0,o.jsx)(`td`,{style:c(e.align[r]??null),children:n(t)},r))},r))})]})})}exports.Markdown=l;
2
2
  //# sourceMappingURL=Markdown.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.cjs","names":[],"sources":["../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the renderer maps every block and inline node the\n * parser can emit to an element, and the length is that mapping — one arm per node\n * kind, no branching to extract.\n */\nimport { Fragment, useMemo, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useScrollOverflow } from \"@/hooks/use-scroll-overflow\";\nimport { cn } from \"@/utils/cn\";\n\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n parseMarkdown,\n type MarkdownAlign,\n type MarkdownBlock,\n type MarkdownInline,\n} from \"./markdown-parse\";\nimport styles from \"./Markdown.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The Markdown source. */\n source: string;\n /**\n * Heading level the document's `#` maps to. Default 2.\n *\n * A comment body rendered inside a page whose `<h1>` is the page title must not\n * emit a second `<h1>`; shifting the whole scale keeps the outline valid.\n */\n headingOffset?: number;\n /** Render fenced code with `CodeBlock` (copy button, line numbers). Default `true`. */\n highlightCode?: boolean;\n /** Show line numbers in fenced code. Default `false`. */\n showLineNumbers?: boolean;\n /** Extra props for every link — `target=\"_blank\"`, an analytics handler. */\n linkProps?: HTMLAttributes<HTMLAnchorElement> & { target?: string; rel?: string };\n}\n\n/** Render inline nodes. */\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode {\n return nodes.map((node, index) => {\n const key = index;\n switch (node.type) {\n case \"text\":\n return <Fragment key={key}>{node.value}</Fragment>;\n case \"strong\":\n return <strong key={key}>{renderInline(node.children)}</strong>;\n case \"em\":\n return <em key={key}>{renderInline(node.children)}</em>;\n case \"del\":\n return <del key={key}>{renderInline(node.children)}</del>;\n case \"code\":\n return (\n <code key={key} className={styles.inlineCode}>\n {node.value}\n </code>\n );\n case \"image\":\n return <img key={key} className={styles.image} src={node.src} alt={node.alt} />;\n case \"break\":\n return <br key={key} />;\n default:\n return null;\n }\n });\n}\n\n/** CSS `text-align` for a table column. */\nfunction alignOf(align: MarkdownAlign): { textAlign: \"left\" | \"center\" | \"right\" } | undefined {\n return align ? { textAlign: align } : undefined;\n}\n\n/**\n * Rendered Markdown, from a node tree — never from an HTML string.\n *\n * `dangerouslySetInnerHTML` appears nowhere in this component. That is what makes\n * rendering somebody else's Markdown safe by construction rather than by a promise\n * about escaping: a `<script>` in a comment is four characters of text, because text\n * is all a React child can be.\n *\n * Link and image URLs go through a scheme **allowlist** (`http`, `https`, `mailto`,\n * `tel`, `sms`, plus relative), so `[click](javascript:alert(1))` renders as plain\n * text. A blocklist would have to enumerate every spelling of `javascript:` and\n * would miss one.\n *\n * @example\n * <Markdown source={comentario.corpo} linkProps={{ target: \"_blank\", rel: \"noreferrer\" }} />\n */\nexport function Markdown({\n source,\n headingOffset = 2,\n highlightCode = true,\n showLineNumbers = false,\n linkProps,\n className,\n ...rest\n}: MarkdownProps) {\n const blocks = useMemo(() => parseMarkdown(source), [source]);\n\n return (\n <div className={cn(styles.root, className)} {...rest}>\n {blocks.map((block, index) => (\n <Block\n key={index}\n block={block}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ))}\n </div>\n );\n}\n\ninterface BlockProps {\n block: MarkdownBlock;\n headingOffset: number;\n highlightCode: boolean;\n showLineNumbers: boolean;\n linkProps?: MarkdownProps[\"linkProps\"];\n}\n\n/** One block, and its children. */\nfunction Block({ block, headingOffset, highlightCode, showLineNumbers, linkProps }: BlockProps) {\n const children = (nodes: readonly MarkdownBlock[]): ReactNode =>\n nodes.map((child, index) => (\n <Block\n key={index}\n block={child}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ));\n\n const inline = (nodes: readonly MarkdownInline[]): ReactNode =>\n nodes.map((node, index) =>\n node.type === \"link\" ? (\n <a key={index} className={styles.link} href={node.href} {...linkProps}>\n {renderInline(node.children)}\n </a>\n ) : (\n <Fragment key={index}>{renderInline([node])}</Fragment>\n ),\n );\n\n switch (block.type) {\n case \"heading\": {\n const level = Math.min(6, Math.max(1, block.level + headingOffset - 1));\n const Tag = `h${level}` as \"h1\";\n return (\n <Tag className={styles[`h${level}` as keyof typeof styles]}>\n {inline(block.children)}\n </Tag>\n );\n }\n case \"paragraph\":\n return <p>{inline(block.children)}</p>;\n case \"code\":\n return highlightCode ? (\n <CodeBlock\n code={block.value}\n language={block.language ?? undefined}\n showLineNumbers={showLineNumbers}\n />\n ) : (\n <pre>\n <code>{block.value}</code>\n </pre>\n );\n case \"quote\":\n return <blockquote className={styles.quote}>{children(block.children)}</blockquote>;\n case \"rule\":\n return <hr className={styles.rule} />;\n case \"list\":\n return block.ordered ? (\n <ol className={styles.list} start={block.start === 1 ? undefined : block.start}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ol>\n ) : (\n <ul className={styles.list}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ul>\n );\n case \"table\":\n return <Table block={block} inline={inline} />;\n default:\n return null;\n }\n}\n\n/**\n * A table, in its own scroll box.\n *\n * The box only becomes a tab stop while it actually overflows: a scroll container\n * with nothing focusable inside cannot be reached by keyboard, and adding the stop\n * unconditionally would pollute the tab order with one entry per table on the page.\n */\nfunction Table({\n block,\n inline,\n}: {\n block: Extract<MarkdownBlock, { type: \"table\" }>;\n inline: (nodes: readonly MarkdownInline[]) => ReactNode;\n}) {\n const scroll = useRef<HTMLDivElement | null>(null);\n const overflowing = useScrollOverflow(scroll, \"horizontal\");\n\n return (\n <div\n ref={scroll}\n className={styles.tableScroll}\n tabIndex={overflowing ? 0 : undefined}\n role={overflowing ? \"region\" : undefined}\n aria-label={overflowing ? \"Tabela rolável\" : undefined}\n >\n <table className={styles.table}>\n <thead>\n <tr>\n {block.head.map((cell, index) => (\n <th key={index} style={alignOf(block.align[index] ?? null)}>\n {inline(cell)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row.map((cell, cellIndex) => (\n <td key={cellIndex} style={alignOf(block.align[cellIndex] ?? null)}>\n {inline(cell)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Markdown.cjs","names":[],"sources":["../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the renderer maps every block and inline node the\n * parser can emit to an element, and the length is that mapping — one arm per node\n * kind, no branching to extract.\n */\nimport { Fragment, useMemo, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useScrollOverflow } from \"@/hooks/use-scroll-overflow\";\nimport { cn } from \"@/utils/cn\";\n\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n parseMarkdown,\n type MarkdownAlign,\n type MarkdownBlock,\n type MarkdownInline,\n} from \"./markdown-parse\";\nimport styles from \"./Markdown.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The Markdown source. */\n source: string;\n /**\n * Heading level the document's `#` maps to. Default 2.\n *\n * A comment body rendered inside a page whose `<h1>` is the page title must not\n * emit a second `<h1>`; shifting the whole scale keeps the outline valid.\n */\n headingOffset?: number;\n /** Render fenced code with `CodeBlock` (copy button, line numbers). Default `true`. */\n highlightCode?: boolean;\n /** Show line numbers in fenced code. Default `false`. */\n showLineNumbers?: boolean;\n /** Extra props for every link — `target=\"_blank\"`, an analytics handler. */\n linkProps?: HTMLAttributes<HTMLAnchorElement> & { target?: string; rel?: string };\n}\n\n/** Render inline nodes. */\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode {\n return nodes.map((node, index) => {\n const key = index;\n switch (node.type) {\n case \"text\":\n return <Fragment key={key}>{node.value}</Fragment>;\n case \"strong\":\n return <strong key={key}>{renderInline(node.children)}</strong>;\n case \"em\":\n return <em key={key}>{renderInline(node.children)}</em>;\n case \"del\":\n return <del key={key}>{renderInline(node.children)}</del>;\n case \"code\":\n return (\n <code key={key} className={styles.inlineCode}>\n {node.value}\n </code>\n );\n case \"image\":\n return <img key={key} className={styles.image} src={node.src} alt={node.alt} />;\n case \"break\":\n return <br key={key} />;\n default:\n return null;\n }\n });\n}\n\n/** CSS `text-align` for a table column. */\nfunction alignOf(align: MarkdownAlign): { textAlign: \"left\" | \"center\" | \"right\" } | undefined {\n return align ? { textAlign: align } : undefined;\n}\n\n/**\n * Rendered Markdown, from a node tree — never from an HTML string.\n *\n * `dangerouslySetInnerHTML` appears nowhere in this component. That is what makes\n * rendering somebody else's Markdown safe by construction rather than by a promise\n * about escaping: a `<script>` in a comment is four characters of text, because text\n * is all a React child can be.\n *\n * Link and image URLs go through a scheme **allowlist** (`http`, `https`, `mailto`,\n * `tel`, `sms`, plus relative), so `[click](javascript:alert(1))` renders as plain\n * text. A blocklist would have to enumerate every spelling of `javascript:` and\n * would miss one.\n *\n * @example\n * <Markdown source={comentario.corpo} linkProps={{ target: \"_blank\", rel: \"noreferrer\" }} />\n */\nexport function Markdown({\n source,\n headingOffset = 2,\n highlightCode = true,\n showLineNumbers = false,\n linkProps,\n className,\n ...rest\n}: MarkdownProps) {\n const blocks = useMemo(() => parseMarkdown(source), [source]);\n\n return (\n <div className={cn(styles.root, className)} {...rest}>\n {blocks.map((block, index) => (\n <Block\n key={index}\n block={block}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ))}\n </div>\n );\n}\n\ninterface BlockProps {\n block: MarkdownBlock;\n headingOffset: number;\n highlightCode: boolean;\n showLineNumbers: boolean;\n linkProps?: MarkdownProps[\"linkProps\"];\n}\n\n/** One block, and its children. */\nfunction Block({ block, headingOffset, highlightCode, showLineNumbers, linkProps }: BlockProps) {\n const children = (nodes: readonly MarkdownBlock[]): ReactNode =>\n nodes.map((child, index) => (\n <Block\n key={index}\n block={child}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ));\n\n const inline = (nodes: readonly MarkdownInline[]): ReactNode =>\n nodes.map((node, index) =>\n node.type === \"link\" ? (\n <a key={index} className={styles.link} href={node.href} {...linkProps}>\n {renderInline(node.children)}\n </a>\n ) : (\n <Fragment key={index}>{renderInline([node])}</Fragment>\n ),\n );\n\n switch (block.type) {\n case \"heading\": {\n const level = Math.min(6, Math.max(1, block.level + headingOffset - 1));\n const Tag = `h${level}` as \"h1\";\n return (\n <Tag className={styles[`h${level}` as keyof typeof styles]}>\n {inline(block.children)}\n </Tag>\n );\n }\n case \"paragraph\":\n return <p>{inline(block.children)}</p>;\n case \"code\":\n return highlightCode ? (\n <CodeBlock\n code={block.value}\n language={block.language ?? undefined}\n showLineNumbers={showLineNumbers}\n />\n ) : (\n <pre>\n <code>{block.value}</code>\n </pre>\n );\n case \"quote\":\n return <blockquote className={styles.quote}>{children(block.children)}</blockquote>;\n case \"rule\":\n return <hr className={styles.rule} />;\n case \"list\":\n return block.ordered ? (\n <ol className={styles.list} start={block.start === 1 ? undefined : block.start}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ol>\n ) : (\n <ul className={styles.list}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ul>\n );\n case \"table\":\n return <Table block={block} inline={inline} />;\n default:\n return null;\n }\n}\n\n/**\n * A table, in its own scroll box.\n *\n * The box only becomes a tab stop while it actually overflows: a scroll container\n * with nothing focusable inside cannot be reached by keyboard, and adding the stop\n * unconditionally would pollute the tab order with one entry per table on the page.\n */\nfunction Table({\n block,\n inline,\n}: {\n block: Extract<MarkdownBlock, { type: \"table\" }>;\n inline: (nodes: readonly MarkdownInline[]) => ReactNode;\n}) {\n const scroll = useRef<HTMLDivElement | null>(null);\n const overflowing = useScrollOverflow(scroll, \"horizontal\");\n\n return (\n <div\n ref={scroll}\n className={styles.tableScroll}\n tabIndex={overflowing ? 0 : undefined}\n role={overflowing ? \"region\" : undefined}\n aria-label={overflowing ? \"Tabela rolável\" : undefined}\n >\n <table className={styles.table}>\n <thead>\n <tr>\n {block.head.map((cell, index) => (\n <th key={index} style={alignOf(block.align[index] ?? null)}>\n {inline(cell)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row.map((cell, cellIndex) => (\n <td key={cellIndex} style={alignOf(block.align[cellIndex] ?? null)}>\n {inline(cell)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -60,8 +60,8 @@ function p({ block: e, headingOffset: t, highlightCode: r, showLineNumbers: o, l
60
60
  }, t) : /* @__PURE__ */ c(a, { children: u([e]) }, t));
61
61
  switch (e.type) {
62
62
  case "heading": {
63
- let n = Math.min(6, Math.max(1, e.level + t - 1));
64
- return /* @__PURE__ */ c(`h${n}`, {
63
+ let n = Math.min(6, Math.max(1, e.level + t - 1)), r = `h${n}`;
64
+ return /* @__PURE__ */ c(r, {
65
65
  className: i[`h${n}`],
66
66
  children: d(e.children)
67
67
  });
@@ -1 +1 @@
1
- {"version":3,"file":"Markdown.js","names":[],"sources":["../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the renderer maps every block and inline node the\n * parser can emit to an element, and the length is that mapping — one arm per node\n * kind, no branching to extract.\n */\nimport { Fragment, useMemo, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useScrollOverflow } from \"@/hooks/use-scroll-overflow\";\nimport { cn } from \"@/utils/cn\";\n\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n parseMarkdown,\n type MarkdownAlign,\n type MarkdownBlock,\n type MarkdownInline,\n} from \"./markdown-parse\";\nimport styles from \"./Markdown.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The Markdown source. */\n source: string;\n /**\n * Heading level the document's `#` maps to. Default 2.\n *\n * A comment body rendered inside a page whose `<h1>` is the page title must not\n * emit a second `<h1>`; shifting the whole scale keeps the outline valid.\n */\n headingOffset?: number;\n /** Render fenced code with `CodeBlock` (copy button, line numbers). Default `true`. */\n highlightCode?: boolean;\n /** Show line numbers in fenced code. Default `false`. */\n showLineNumbers?: boolean;\n /** Extra props for every link — `target=\"_blank\"`, an analytics handler. */\n linkProps?: HTMLAttributes<HTMLAnchorElement> & { target?: string; rel?: string };\n}\n\n/** Render inline nodes. */\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode {\n return nodes.map((node, index) => {\n const key = index;\n switch (node.type) {\n case \"text\":\n return <Fragment key={key}>{node.value}</Fragment>;\n case \"strong\":\n return <strong key={key}>{renderInline(node.children)}</strong>;\n case \"em\":\n return <em key={key}>{renderInline(node.children)}</em>;\n case \"del\":\n return <del key={key}>{renderInline(node.children)}</del>;\n case \"code\":\n return (\n <code key={key} className={styles.inlineCode}>\n {node.value}\n </code>\n );\n case \"image\":\n return <img key={key} className={styles.image} src={node.src} alt={node.alt} />;\n case \"break\":\n return <br key={key} />;\n default:\n return null;\n }\n });\n}\n\n/** CSS `text-align` for a table column. */\nfunction alignOf(align: MarkdownAlign): { textAlign: \"left\" | \"center\" | \"right\" } | undefined {\n return align ? { textAlign: align } : undefined;\n}\n\n/**\n * Rendered Markdown, from a node tree — never from an HTML string.\n *\n * `dangerouslySetInnerHTML` appears nowhere in this component. That is what makes\n * rendering somebody else's Markdown safe by construction rather than by a promise\n * about escaping: a `<script>` in a comment is four characters of text, because text\n * is all a React child can be.\n *\n * Link and image URLs go through a scheme **allowlist** (`http`, `https`, `mailto`,\n * `tel`, `sms`, plus relative), so `[click](javascript:alert(1))` renders as plain\n * text. A blocklist would have to enumerate every spelling of `javascript:` and\n * would miss one.\n *\n * @example\n * <Markdown source={comentario.corpo} linkProps={{ target: \"_blank\", rel: \"noreferrer\" }} />\n */\nexport function Markdown({\n source,\n headingOffset = 2,\n highlightCode = true,\n showLineNumbers = false,\n linkProps,\n className,\n ...rest\n}: MarkdownProps) {\n const blocks = useMemo(() => parseMarkdown(source), [source]);\n\n return (\n <div className={cn(styles.root, className)} {...rest}>\n {blocks.map((block, index) => (\n <Block\n key={index}\n block={block}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ))}\n </div>\n );\n}\n\ninterface BlockProps {\n block: MarkdownBlock;\n headingOffset: number;\n highlightCode: boolean;\n showLineNumbers: boolean;\n linkProps?: MarkdownProps[\"linkProps\"];\n}\n\n/** One block, and its children. */\nfunction Block({ block, headingOffset, highlightCode, showLineNumbers, linkProps }: BlockProps) {\n const children = (nodes: readonly MarkdownBlock[]): ReactNode =>\n nodes.map((child, index) => (\n <Block\n key={index}\n block={child}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ));\n\n const inline = (nodes: readonly MarkdownInline[]): ReactNode =>\n nodes.map((node, index) =>\n node.type === \"link\" ? (\n <a key={index} className={styles.link} href={node.href} {...linkProps}>\n {renderInline(node.children)}\n </a>\n ) : (\n <Fragment key={index}>{renderInline([node])}</Fragment>\n ),\n );\n\n switch (block.type) {\n case \"heading\": {\n const level = Math.min(6, Math.max(1, block.level + headingOffset - 1));\n const Tag = `h${level}` as \"h1\";\n return (\n <Tag className={styles[`h${level}` as keyof typeof styles]}>\n {inline(block.children)}\n </Tag>\n );\n }\n case \"paragraph\":\n return <p>{inline(block.children)}</p>;\n case \"code\":\n return highlightCode ? (\n <CodeBlock\n code={block.value}\n language={block.language ?? undefined}\n showLineNumbers={showLineNumbers}\n />\n ) : (\n <pre>\n <code>{block.value}</code>\n </pre>\n );\n case \"quote\":\n return <blockquote className={styles.quote}>{children(block.children)}</blockquote>;\n case \"rule\":\n return <hr className={styles.rule} />;\n case \"list\":\n return block.ordered ? (\n <ol className={styles.list} start={block.start === 1 ? undefined : block.start}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ol>\n ) : (\n <ul className={styles.list}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ul>\n );\n case \"table\":\n return <Table block={block} inline={inline} />;\n default:\n return null;\n }\n}\n\n/**\n * A table, in its own scroll box.\n *\n * The box only becomes a tab stop while it actually overflows: a scroll container\n * with nothing focusable inside cannot be reached by keyboard, and adding the stop\n * unconditionally would pollute the tab order with one entry per table on the page.\n */\nfunction Table({\n block,\n inline,\n}: {\n block: Extract<MarkdownBlock, { type: \"table\" }>;\n inline: (nodes: readonly MarkdownInline[]) => ReactNode;\n}) {\n const scroll = useRef<HTMLDivElement | null>(null);\n const overflowing = useScrollOverflow(scroll, \"horizontal\");\n\n return (\n <div\n ref={scroll}\n className={styles.tableScroll}\n tabIndex={overflowing ? 0 : undefined}\n role={overflowing ? \"region\" : undefined}\n aria-label={overflowing ? \"Tabela rolável\" : undefined}\n >\n <table className={styles.table}>\n <thead>\n <tr>\n {block.head.map((cell, index) => (\n <th key={index} style={alignOf(block.align[index] ?? null)}>\n {inline(cell)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row.map((cell, cellIndex) => (\n <td key={cellIndex} style={alignOf(block.align[cellIndex] ?? null)}>\n {inline(cell)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Markdown.js","names":[],"sources":["../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the renderer maps every block and inline node the\n * parser can emit to an element, and the length is that mapping — one arm per node\n * kind, no branching to extract.\n */\nimport { Fragment, useMemo, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useScrollOverflow } from \"@/hooks/use-scroll-overflow\";\nimport { cn } from \"@/utils/cn\";\n\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n parseMarkdown,\n type MarkdownAlign,\n type MarkdownBlock,\n type MarkdownInline,\n} from \"./markdown-parse\";\nimport styles from \"./Markdown.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The Markdown source. */\n source: string;\n /**\n * Heading level the document's `#` maps to. Default 2.\n *\n * A comment body rendered inside a page whose `<h1>` is the page title must not\n * emit a second `<h1>`; shifting the whole scale keeps the outline valid.\n */\n headingOffset?: number;\n /** Render fenced code with `CodeBlock` (copy button, line numbers). Default `true`. */\n highlightCode?: boolean;\n /** Show line numbers in fenced code. Default `false`. */\n showLineNumbers?: boolean;\n /** Extra props for every link — `target=\"_blank\"`, an analytics handler. */\n linkProps?: HTMLAttributes<HTMLAnchorElement> & { target?: string; rel?: string };\n}\n\n/** Render inline nodes. */\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode {\n return nodes.map((node, index) => {\n const key = index;\n switch (node.type) {\n case \"text\":\n return <Fragment key={key}>{node.value}</Fragment>;\n case \"strong\":\n return <strong key={key}>{renderInline(node.children)}</strong>;\n case \"em\":\n return <em key={key}>{renderInline(node.children)}</em>;\n case \"del\":\n return <del key={key}>{renderInline(node.children)}</del>;\n case \"code\":\n return (\n <code key={key} className={styles.inlineCode}>\n {node.value}\n </code>\n );\n case \"image\":\n return <img key={key} className={styles.image} src={node.src} alt={node.alt} />;\n case \"break\":\n return <br key={key} />;\n default:\n return null;\n }\n });\n}\n\n/** CSS `text-align` for a table column. */\nfunction alignOf(align: MarkdownAlign): { textAlign: \"left\" | \"center\" | \"right\" } | undefined {\n return align ? { textAlign: align } : undefined;\n}\n\n/**\n * Rendered Markdown, from a node tree — never from an HTML string.\n *\n * `dangerouslySetInnerHTML` appears nowhere in this component. That is what makes\n * rendering somebody else's Markdown safe by construction rather than by a promise\n * about escaping: a `<script>` in a comment is four characters of text, because text\n * is all a React child can be.\n *\n * Link and image URLs go through a scheme **allowlist** (`http`, `https`, `mailto`,\n * `tel`, `sms`, plus relative), so `[click](javascript:alert(1))` renders as plain\n * text. A blocklist would have to enumerate every spelling of `javascript:` and\n * would miss one.\n *\n * @example\n * <Markdown source={comentario.corpo} linkProps={{ target: \"_blank\", rel: \"noreferrer\" }} />\n */\nexport function Markdown({\n source,\n headingOffset = 2,\n highlightCode = true,\n showLineNumbers = false,\n linkProps,\n className,\n ...rest\n}: MarkdownProps) {\n const blocks = useMemo(() => parseMarkdown(source), [source]);\n\n return (\n <div className={cn(styles.root, className)} {...rest}>\n {blocks.map((block, index) => (\n <Block\n key={index}\n block={block}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ))}\n </div>\n );\n}\n\ninterface BlockProps {\n block: MarkdownBlock;\n headingOffset: number;\n highlightCode: boolean;\n showLineNumbers: boolean;\n linkProps?: MarkdownProps[\"linkProps\"];\n}\n\n/** One block, and its children. */\nfunction Block({ block, headingOffset, highlightCode, showLineNumbers, linkProps }: BlockProps) {\n const children = (nodes: readonly MarkdownBlock[]): ReactNode =>\n nodes.map((child, index) => (\n <Block\n key={index}\n block={child}\n headingOffset={headingOffset}\n highlightCode={highlightCode}\n showLineNumbers={showLineNumbers}\n linkProps={linkProps}\n />\n ));\n\n const inline = (nodes: readonly MarkdownInline[]): ReactNode =>\n nodes.map((node, index) =>\n node.type === \"link\" ? (\n <a key={index} className={styles.link} href={node.href} {...linkProps}>\n {renderInline(node.children)}\n </a>\n ) : (\n <Fragment key={index}>{renderInline([node])}</Fragment>\n ),\n );\n\n switch (block.type) {\n case \"heading\": {\n const level = Math.min(6, Math.max(1, block.level + headingOffset - 1));\n const Tag = `h${level}` as \"h1\";\n return (\n <Tag className={styles[`h${level}` as keyof typeof styles]}>\n {inline(block.children)}\n </Tag>\n );\n }\n case \"paragraph\":\n return <p>{inline(block.children)}</p>;\n case \"code\":\n return highlightCode ? (\n <CodeBlock\n code={block.value}\n language={block.language ?? undefined}\n showLineNumbers={showLineNumbers}\n />\n ) : (\n <pre>\n <code>{block.value}</code>\n </pre>\n );\n case \"quote\":\n return <blockquote className={styles.quote}>{children(block.children)}</blockquote>;\n case \"rule\":\n return <hr className={styles.rule} />;\n case \"list\":\n return block.ordered ? (\n <ol className={styles.list} start={block.start === 1 ? undefined : block.start}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ol>\n ) : (\n <ul className={styles.list}>\n {block.items.map((item, index) => (\n <li key={index} className={styles.item}>\n {children(item)}\n </li>\n ))}\n </ul>\n );\n case \"table\":\n return <Table block={block} inline={inline} />;\n default:\n return null;\n }\n}\n\n/**\n * A table, in its own scroll box.\n *\n * The box only becomes a tab stop while it actually overflows: a scroll container\n * with nothing focusable inside cannot be reached by keyboard, and adding the stop\n * unconditionally would pollute the tab order with one entry per table on the page.\n */\nfunction Table({\n block,\n inline,\n}: {\n block: Extract<MarkdownBlock, { type: \"table\" }>;\n inline: (nodes: readonly MarkdownInline[]) => ReactNode;\n}) {\n const scroll = useRef<HTMLDivElement | null>(null);\n const overflowing = useScrollOverflow(scroll, \"horizontal\");\n\n return (\n <div\n ref={scroll}\n className={styles.tableScroll}\n tabIndex={overflowing ? 0 : undefined}\n role={overflowing ? \"region\" : undefined}\n aria-label={overflowing ? \"Tabela rolável\" : undefined}\n >\n <table className={styles.table}>\n <thead>\n <tr>\n {block.head.map((cell, index) => (\n <th key={index} style={alignOf(block.align[index] ?? null)}>\n {inline(cell)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>\n {row.map((cell, cellIndex) => (\n <td key={cellIndex} style={alignOf(block.align[cellIndex] ?? null)}>\n {inline(cell)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Masonry.cjs","names":[],"sources":["../../../src/components/Masonry/Masonry.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { columnsFor, distribute } from \"./masonry-layout\";\nimport styles from \"./Masonry.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MasonryProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** What to lay out. */\n items: readonly T[];\n /** Render one card. */\n children: (item: T, index: number) => ReactNode;\n /** Stable key per item. Index is a fallback nobody should rely on. */\n itemKey?: (item: T, index: number) => string | number;\n /**\n * Column count: a fixed number, or a `width → columns` map read as\n * \"from this width up\". Default `{ 0: 1, 640: 2, 1024: 3 }`.\n */\n columns?: number | Record<number, number>;\n /** Gap between cards, any CSS length. Default `var(--tempest-space-4)`. */\n gap?: string;\n}\n\n/** Default breakpoints: one column on a phone, three on a desktop. */\nconst DEFAULT_COLUMNS: Record<number, number> = { 0: 1, 640: 2, 1024: 3 };\n\n/**\n * Masonry layout: cards of uneven height packed into columns with an even bottom\n * edge.\n *\n * Measures the rendered cards and deals each one into the shortest column, rather\n * than using CSS `columns` or `grid-auto-flow: dense`. Both of those are one line\n * of CSS and neither does this job: CSS `columns` breaks a card across the column\n * boundary, and a dense grid keeps every row the height of its tallest cell, which\n * is the ragged bottom edge people reach for masonry to avoid.\n *\n * @example\n * <Masonry items={fotos} itemKey={(foto) => foto.id} columns={{ 0: 1, 700: 2, 1100: 4 }}>\n * {(foto) => <img src={foto.url} alt={foto.alt} />}\n * </Masonry>\n */\nexport function Masonry<T>({\n items,\n children,\n itemKey,\n columns = DEFAULT_COLUMNS,\n gap,\n className,\n style,\n ...rest\n}: MasonryProps<T>) {\n const wrapper = useRef<HTMLDivElement | null>(null);\n const cells = useRef<Map<number, HTMLElement>>(new Map());\n const [width, setWidth] = useState(0);\n const [heights, setHeights] = useState<number[]>([]);\n\n const columnCount = columnsFor(width, columns);\n\n /**\n * Track the container width, not the viewport.\n *\n * A masonry inside a drawer or a two-column page is narrower than the window,\n * and a media query would give it desktop columns at 300px wide. `ResizeObserver`\n * is what makes the breakpoint map mean \"this container\", which is the only\n * useful reading.\n */\n useEffect(() => {\n const node = wrapper.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n setWidth(entry.contentRect.width);\n });\n observer.observe(node);\n setWidth(node.getBoundingClientRect().width);\n return () => observer.disconnect();\n }, []);\n\n /**\n * Re-read every card's height, and store it only when something moved.\n *\n * The equality check is what stops the loop: writing a new array on every pass\n * would re-render, re-measure and write again. The first render lays out in\n * source order with weight 1 each — never blank — and this pass re-deals with\n * real numbers.\n */\n const measure = useCallback(() => {\n const next = items.map((_, index) => {\n const node = cells.current.get(index);\n return node ? node.getBoundingClientRect().height : 1;\n });\n setHeights((current) =>\n current.length === next.length && current.every((value, i) => value === next[i])\n ? current\n : next,\n );\n }, [items]);\n\n useLayoutEffect(measure, [measure, columnCount, width]);\n\n /**\n * Re-measure when a card changes size on its own — an image finishing its\n * download is the common one, and it is exactly the case a height measured at\n * mount gets wrong.\n */\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n for (const node of cells.current.values()) observer.observe(node);\n return () => observer.disconnect();\n }, [measure, columnCount]);\n\n const layout = useMemo(\n () =>\n distribute(\n items.map((_, index) => heights[index] ?? 1),\n columnCount,\n ),\n [items, heights, columnCount],\n );\n\n return (\n <div\n ref={wrapper}\n className={cn(styles.wrapper, className)}\n style={{ ...style, ...(gap ? { \"--tempest-masonry-gap\": gap } : {}) }}\n {...rest}\n >\n {layout.map((columnItems, column) => (\n <div key={column} className={styles.column}>\n {columnItems.map((index) => {\n const item = items[index];\n return (\n <div\n key={itemKey ? itemKey(item, index) : index}\n className={styles.item}\n ref={(node) => {\n if (node) cells.current.set(index, node);\n else cells.current.delete(index);\n }}\n >\n {children(item, index)}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"gKAoCA,IAAM,EAA0C,CAAE,EAAG,EAAG,IAAK,EAAG,KAAM,CAAE,EAiBxE,SAAgB,EAAW,CACvB,QACA,WACA,UACA,UAAU,EACV,MACA,YACA,QACA,GAAG,GACa,CAChB,IAAM,GAAA,EAAA,EAAA,OAAA,CAAwC,IAAI,EAC5C,GAAA,EAAA,EAAA,OAAA,CAAyC,IAAI,GAAK,EAClD,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAqB,CAAC,EAC9B,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAAiC,CAAC,CAAC,EAE7C,EAAc,EAAA,WAAW,EAAO,CAAO,GAU7C,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAO,EAAQ,QACrB,GAAI,CAAC,GAAQ,OAAO,eAAmB,IAAa,OACpD,IAAM,EAAW,IAAI,gBAAgB,CAAC,KAAW,CAC7C,EAAS,EAAM,YAAY,KAAK,CACpC,CAAC,EAGD,OAFA,EAAS,QAAQ,CAAI,EACrB,EAAS,EAAK,sBAAsB,CAAC,CAAC,KAAK,MAC9B,EAAS,WAAW,CACrC,EAAG,CAAC,CAAC,EAUL,IAAM,GAAA,EAAA,EAAA,YAAA,KAA4B,CAC9B,IAAM,EAAO,EAAM,KAAK,EAAG,IAAU,CACjC,IAAM,EAAO,EAAM,QAAQ,IAAI,CAAK,EACpC,OAAO,EAAO,EAAK,sBAAsB,CAAC,CAAC,OAAS,CACxD,CAAC,EACD,EAAY,GACR,EAAQ,SAAW,EAAK,QAAU,EAAQ,OAAO,EAAO,IAAM,IAAU,EAAK,EAAE,EACzE,EACA,CACV,CACJ,EAAG,CAAC,CAAK,CAAC,GAEV,EAAA,EAAA,gBAAA,CAAgB,EAAS,CAAC,EAAS,EAAa,CAAK,CAAC,GAOtD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,eAAmB,IAAa,OAC3C,IAAM,EAAW,IAAI,eAAe,CAAO,EAC3C,IAAK,IAAM,KAAQ,EAAM,QAAQ,OAAO,EAAG,EAAS,QAAQ,CAAI,EAChE,UAAa,EAAS,WAAW,CACrC,EAAG,CAAC,EAAS,CAAW,CAAC,EAEzB,IAAM,GAAA,EAAA,EAAA,QAAA,KAEE,EAAA,WACI,EAAM,KAAK,EAAG,IAAU,EAAQ,IAAU,CAAC,EAC3C,CACJ,EACJ,CAAC,EAAO,EAAS,CAAW,CAChC,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,IAAK,EACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EACvC,MAAO,CAAE,GAAG,EAAO,GAAI,EAAM,CAAE,wBAAyB,CAAI,EAAI,CAAC,CAAG,EACpE,GAAI,WAEH,EAAO,KAAK,EAAa,KACtB,EAAA,EAAA,IAAA,CAAC,MAAD,CAAkB,UAAW,EAAA,QAAO,gBAC/B,EAAY,IAAK,GAAU,CACxB,IAAM,EAAO,EAAM,GACnB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAEI,UAAW,EAAA,QAAO,KAClB,IAAM,GAAS,CACP,EAAM,EAAM,QAAQ,IAAI,EAAO,CAAI,EAClC,EAAM,QAAQ,OAAO,CAAK,CACnC,WAEC,EAAS,EAAM,CAAK,CACpB,EARI,EAAU,EAAQ,EAAM,CAAK,EAAI,CAQrC,CAEb,CAAC,CACA,EAhBK,CAgBL,CACR,CACA,CAAA,CAEb"}
1
+ {"version":3,"file":"Masonry.cjs","names":[],"sources":["../../../src/components/Masonry/Masonry.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { columnsFor, distribute } from \"./masonry-layout\";\nimport styles from \"./Masonry.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MasonryProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** What to lay out. */\n items: readonly T[];\n /** Render one card. */\n children: (item: T, index: number) => ReactNode;\n /** Stable key per item. Index is a fallback nobody should rely on. */\n itemKey?: (item: T, index: number) => string | number;\n /**\n * Column count: a fixed number, or a `width → columns` map read as\n * \"from this width up\". Default `{ 0: 1, 640: 2, 1024: 3 }`.\n */\n columns?: number | Record<number, number>;\n /** Gap between cards, any CSS length. Default `var(--tempest-space-4)`. */\n gap?: string;\n}\n\n/** Default breakpoints: one column on a phone, three on a desktop. */\nconst DEFAULT_COLUMNS: Record<number, number> = { 0: 1, 640: 2, 1024: 3 };\n\n/**\n * Masonry layout: cards of uneven height packed into columns with an even bottom\n * edge.\n *\n * Measures the rendered cards and deals each one into the shortest column, rather\n * than using CSS `columns` or `grid-auto-flow: dense`. Both of those are one line\n * of CSS and neither does this job: CSS `columns` breaks a card across the column\n * boundary, and a dense grid keeps every row the height of its tallest cell, which\n * is the ragged bottom edge people reach for masonry to avoid.\n *\n * @example\n * <Masonry items={fotos} itemKey={(foto) => foto.id} columns={{ 0: 1, 700: 2, 1100: 4 }}>\n * {(foto) => <img src={foto.url} alt={foto.alt} />}\n * </Masonry>\n */\nexport function Masonry<T>({\n items,\n children,\n itemKey,\n columns = DEFAULT_COLUMNS,\n gap,\n className,\n style,\n ...rest\n}: MasonryProps<T>) {\n const wrapper = useRef<HTMLDivElement | null>(null);\n const cells = useRef<Map<number, HTMLElement>>(new Map());\n const [width, setWidth] = useState(0);\n const [heights, setHeights] = useState<number[]>([]);\n\n const columnCount = columnsFor(width, columns);\n\n /**\n * Track the container width, not the viewport.\n *\n * A masonry inside a drawer or a two-column page is narrower than the window,\n * and a media query would give it desktop columns at 300px wide. `ResizeObserver`\n * is what makes the breakpoint map mean \"this container\", which is the only\n * useful reading.\n */\n useEffect(() => {\n const node = wrapper.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n setWidth(entry.contentRect.width);\n });\n observer.observe(node);\n setWidth(node.getBoundingClientRect().width);\n return () => observer.disconnect();\n }, []);\n\n /**\n * Re-read every card's height, and store it only when something moved.\n *\n * The equality check is what stops the loop: writing a new array on every pass\n * would re-render, re-measure and write again. The first render lays out in\n * source order with weight 1 each — never blank — and this pass re-deals with\n * real numbers.\n */\n const measure = useCallback(() => {\n const next = items.map((_, index) => {\n const node = cells.current.get(index);\n return node ? node.getBoundingClientRect().height : 1;\n });\n setHeights((current) =>\n current.length === next.length && current.every((value, i) => value === next[i])\n ? current\n : next,\n );\n }, [items]);\n\n useLayoutEffect(measure, [measure, columnCount, width]);\n\n /**\n * Re-measure when a card changes size on its own — an image finishing its\n * download is the common one, and it is exactly the case a height measured at\n * mount gets wrong.\n */\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n for (const node of cells.current.values()) observer.observe(node);\n return () => observer.disconnect();\n }, [measure, columnCount]);\n\n const layout = useMemo(\n () =>\n distribute(\n items.map((_, index) => heights[index] ?? 1),\n columnCount,\n ),\n [items, heights, columnCount],\n );\n\n return (\n <div\n ref={wrapper}\n className={cn(styles.wrapper, className)}\n style={{ ...style, ...(gap ? { \"--tempest-masonry-gap\": gap } : {}) }}\n {...rest}\n >\n {layout.map((columnItems, column) => (\n <div key={column} className={styles.column}>\n {columnItems.map((index) => {\n const item = items[index];\n return (\n <div\n key={itemKey ? itemKey(item, index) : index}\n className={styles.item}\n ref={(node) => {\n if (node) cells.current.set(index, node);\n else cells.current.delete(index);\n }}\n >\n {children(item, index)}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"gKAoCA,IAAM,EAA0C,CAAE,EAAG,EAAG,IAAK,EAAG,KAAM,CAAE,EAiBxE,SAAgB,EAAW,CACvB,QACA,WACA,UACA,UAAU,EACV,MACA,YACA,QACA,GAAG,GACa,CAChB,IAAM,GAAA,EAAU,EAAA,OAAA,CAA8B,IAAI,EAC5C,GAAA,EAAQ,EAAA,OAAA,CAAiC,IAAI,GAAK,EAClD,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAS,CAAC,EAC9B,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAmB,CAAC,CAAC,EAE7C,EAAc,EAAA,WAAW,EAAO,CAAO,GAU7C,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAO,EAAQ,QACrB,GAAI,CAAC,GAAQ,OAAO,eAAmB,IAAa,OACpD,IAAM,EAAW,IAAI,gBAAgB,CAAC,KAAW,CAC7C,EAAS,EAAM,YAAY,KAAK,CACpC,CAAC,EAGD,OAFA,EAAS,QAAQ,CAAI,EACrB,EAAS,EAAK,sBAAsB,CAAC,CAAC,KAAK,MAC9B,EAAS,WAAW,CACrC,EAAG,CAAC,CAAC,EAUL,IAAM,GAAA,EAAU,EAAA,YAAA,KAAkB,CAC9B,IAAM,EAAO,EAAM,KAAK,EAAG,IAAU,CACjC,IAAM,EAAO,EAAM,QAAQ,IAAI,CAAK,EACpC,OAAO,EAAO,EAAK,sBAAsB,CAAC,CAAC,OAAS,CACxD,CAAC,EACD,EAAY,GACR,EAAQ,SAAW,EAAK,QAAU,EAAQ,OAAO,EAAO,IAAM,IAAU,EAAK,EAAE,EACzE,EACA,CACV,CACJ,EAAG,CAAC,CAAK,CAAC,GAEV,EAAA,EAAA,gBAAA,CAAgB,EAAS,CAAC,EAAS,EAAa,CAAK,CAAC,GAOtD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,eAAmB,IAAa,OAC3C,IAAM,EAAW,IAAI,eAAe,CAAO,EAC3C,IAAK,IAAM,KAAQ,EAAM,QAAQ,OAAO,EAAG,EAAS,QAAQ,CAAI,EAChE,UAAa,EAAS,WAAW,CACrC,EAAG,CAAC,EAAS,CAAW,CAAC,EAEzB,IAAM,GAAA,EAAS,EAAA,QAAA,KAEP,EAAA,WACI,EAAM,KAAK,EAAG,IAAU,EAAQ,IAAU,CAAC,EAC3C,CACJ,EACJ,CAAC,EAAO,EAAS,CAAW,CAChC,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,IAAK,EACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EACvC,MAAO,CAAE,GAAG,EAAO,GAAI,EAAM,CAAE,wBAAyB,CAAI,EAAI,CAAC,CAAG,EACpE,GAAI,EAEH,SAAA,EAAO,KAAK,EAAa,KACtB,EAAA,EAAA,IAAA,CAAC,MAAD,CAAkB,UAAW,EAAA,QAAO,OAC/B,SAAA,EAAY,IAAK,GAAU,CACxB,IAAM,EAAO,EAAM,GACnB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAEI,UAAW,EAAA,QAAO,KAClB,IAAM,GAAS,CACP,EAAM,EAAM,QAAQ,IAAI,EAAO,CAAI,EAClC,EAAM,QAAQ,OAAO,CAAK,CACnC,EAEC,SAAA,EAAS,EAAM,CAAK,CACpB,EARI,EAAU,EAAQ,EAAM,CAAK,EAAI,CAQrC,CAEb,CAAC,CACA,EAhBK,CAgBL,CACR,CACA,CAAA,CAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"Masonry.js","names":[],"sources":["../../../src/components/Masonry/Masonry.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { columnsFor, distribute } from \"./masonry-layout\";\nimport styles from \"./Masonry.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MasonryProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** What to lay out. */\n items: readonly T[];\n /** Render one card. */\n children: (item: T, index: number) => ReactNode;\n /** Stable key per item. Index is a fallback nobody should rely on. */\n itemKey?: (item: T, index: number) => string | number;\n /**\n * Column count: a fixed number, or a `width → columns` map read as\n * \"from this width up\". Default `{ 0: 1, 640: 2, 1024: 3 }`.\n */\n columns?: number | Record<number, number>;\n /** Gap between cards, any CSS length. Default `var(--tempest-space-4)`. */\n gap?: string;\n}\n\n/** Default breakpoints: one column on a phone, three on a desktop. */\nconst DEFAULT_COLUMNS: Record<number, number> = { 0: 1, 640: 2, 1024: 3 };\n\n/**\n * Masonry layout: cards of uneven height packed into columns with an even bottom\n * edge.\n *\n * Measures the rendered cards and deals each one into the shortest column, rather\n * than using CSS `columns` or `grid-auto-flow: dense`. Both of those are one line\n * of CSS and neither does this job: CSS `columns` breaks a card across the column\n * boundary, and a dense grid keeps every row the height of its tallest cell, which\n * is the ragged bottom edge people reach for masonry to avoid.\n *\n * @example\n * <Masonry items={fotos} itemKey={(foto) => foto.id} columns={{ 0: 1, 700: 2, 1100: 4 }}>\n * {(foto) => <img src={foto.url} alt={foto.alt} />}\n * </Masonry>\n */\nexport function Masonry<T>({\n items,\n children,\n itemKey,\n columns = DEFAULT_COLUMNS,\n gap,\n className,\n style,\n ...rest\n}: MasonryProps<T>) {\n const wrapper = useRef<HTMLDivElement | null>(null);\n const cells = useRef<Map<number, HTMLElement>>(new Map());\n const [width, setWidth] = useState(0);\n const [heights, setHeights] = useState<number[]>([]);\n\n const columnCount = columnsFor(width, columns);\n\n /**\n * Track the container width, not the viewport.\n *\n * A masonry inside a drawer or a two-column page is narrower than the window,\n * and a media query would give it desktop columns at 300px wide. `ResizeObserver`\n * is what makes the breakpoint map mean \"this container\", which is the only\n * useful reading.\n */\n useEffect(() => {\n const node = wrapper.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n setWidth(entry.contentRect.width);\n });\n observer.observe(node);\n setWidth(node.getBoundingClientRect().width);\n return () => observer.disconnect();\n }, []);\n\n /**\n * Re-read every card's height, and store it only when something moved.\n *\n * The equality check is what stops the loop: writing a new array on every pass\n * would re-render, re-measure and write again. The first render lays out in\n * source order with weight 1 each — never blank — and this pass re-deals with\n * real numbers.\n */\n const measure = useCallback(() => {\n const next = items.map((_, index) => {\n const node = cells.current.get(index);\n return node ? node.getBoundingClientRect().height : 1;\n });\n setHeights((current) =>\n current.length === next.length && current.every((value, i) => value === next[i])\n ? current\n : next,\n );\n }, [items]);\n\n useLayoutEffect(measure, [measure, columnCount, width]);\n\n /**\n * Re-measure when a card changes size on its own — an image finishing its\n * download is the common one, and it is exactly the case a height measured at\n * mount gets wrong.\n */\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n for (const node of cells.current.values()) observer.observe(node);\n return () => observer.disconnect();\n }, [measure, columnCount]);\n\n const layout = useMemo(\n () =>\n distribute(\n items.map((_, index) => heights[index] ?? 1),\n columnCount,\n ),\n [items, heights, columnCount],\n );\n\n return (\n <div\n ref={wrapper}\n className={cn(styles.wrapper, className)}\n style={{ ...style, ...(gap ? { \"--tempest-masonry-gap\": gap } : {}) }}\n {...rest}\n >\n {layout.map((columnItems, column) => (\n <div key={column} className={styles.column}>\n {columnItems.map((index) => {\n const item = items[index];\n return (\n <div\n key={itemKey ? itemKey(item, index) : index}\n className={styles.item}\n ref={(node) => {\n if (node) cells.current.set(index, node);\n else cells.current.delete(index);\n }}\n >\n {children(item, index)}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;AAoCA,IAAM,IAA0C;CAAE,GAAG;CAAG,KAAK;CAAG,MAAM;AAAE;AAiBxE,SAAgB,EAAW,EACvB,UACA,aACA,YACA,aAAU,GACV,QACA,cACA,UACA,GAAG,KACa;CAChB,IAAM,IAAU,EAA8B,IAAI,GAC5C,IAAQ,kBAAiC,IAAI,IAAI,CAAC,GAClD,CAAC,GAAO,KAAY,EAAS,CAAC,GAC9B,CAAC,GAAS,KAAc,EAAmB,CAAC,CAAC,GAE7C,IAAc,EAAW,GAAO,CAAO;CAU7C,QAAgB;EACZ,IAAM,IAAO,EAAQ;EACrB,IAAI,CAAC,KAAQ,OAAO,iBAAmB,KAAa;EACpD,IAAM,IAAW,IAAI,gBAAgB,CAAC,OAAW;GAC7C,EAAS,EAAM,YAAY,KAAK;EACpC,CAAC;EAGD,OAFA,EAAS,QAAQ,CAAI,GACrB,EAAS,EAAK,sBAAsB,CAAC,CAAC,KAAK,SAC9B,EAAS,WAAW;CACrC,GAAG,CAAC,CAAC;CAUL,IAAM,IAAU,QAAkB;EAC9B,IAAM,IAAO,EAAM,KAAK,GAAG,MAAU;GACjC,IAAM,IAAO,EAAM,QAAQ,IAAI,CAAK;GACpC,OAAO,IAAO,EAAK,sBAAsB,CAAC,CAAC,SAAS;EACxD,CAAC;EACD,GAAY,MACR,EAAQ,WAAW,EAAK,UAAU,EAAQ,OAAO,GAAO,MAAM,MAAU,EAAK,EAAE,IACzE,IACA,CACV;CACJ,GAAG,CAAC,CAAK,CAAC;CASV,AAPA,EAAgB,GAAS;EAAC;EAAS;EAAa;CAAK,CAAC,GAOtD,QAAgB;EACZ,IAAI,OAAO,iBAAmB,KAAa;EAC3C,IAAM,IAAW,IAAI,eAAe,CAAO;EAC3C,KAAK,IAAM,KAAQ,EAAM,QAAQ,OAAO,GAAG,EAAS,QAAQ,CAAI;EAChE,aAAa,EAAS,WAAW;CACrC,GAAG,CAAC,GAAS,CAAW,CAAC;CAEzB,IAAM,IAAS,QAEP,EACI,EAAM,KAAK,GAAG,MAAU,EAAQ,MAAU,CAAC,GAC3C,CACJ,GACJ;EAAC;EAAO;EAAS;CAAW,CAChC;CAEA,OACI,kBAAC,OAAD;EACI,KAAK;EACL,WAAW,EAAG,EAAO,SAAS,CAAS;EACvC,OAAO;GAAE,GAAG;GAAO,GAAI,IAAM,EAAE,yBAAyB,EAAI,IAAI,CAAC;EAAG;EACpE,GAAI;YAEH,EAAO,KAAK,GAAa,MACtB,kBAAC,OAAD;GAAkB,WAAW,EAAO;aAC/B,EAAY,KAAK,MAAU;IACxB,IAAM,IAAO,EAAM;IACnB,OACI,kBAAC,OAAD;KAEI,WAAW,EAAO;KAClB,MAAM,MAAS;MACX,AAAI,IAAM,EAAM,QAAQ,IAAI,GAAO,CAAI,IAClC,EAAM,QAAQ,OAAO,CAAK;KACnC;eAEC,EAAS,GAAM,CAAK;IACpB,GARI,IAAU,EAAQ,GAAM,CAAK,IAAI,CAQrC;GAEb,CAAC;EACA,GAhBK,CAgBL,CACR;CACA,CAAA;AAEb"}
1
+ {"version":3,"file":"Masonry.js","names":[],"sources":["../../../src/components/Masonry/Masonry.tsx"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { columnsFor, distribute } from \"./masonry-layout\";\nimport styles from \"./Masonry.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MasonryProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** What to lay out. */\n items: readonly T[];\n /** Render one card. */\n children: (item: T, index: number) => ReactNode;\n /** Stable key per item. Index is a fallback nobody should rely on. */\n itemKey?: (item: T, index: number) => string | number;\n /**\n * Column count: a fixed number, or a `width → columns` map read as\n * \"from this width up\". Default `{ 0: 1, 640: 2, 1024: 3 }`.\n */\n columns?: number | Record<number, number>;\n /** Gap between cards, any CSS length. Default `var(--tempest-space-4)`. */\n gap?: string;\n}\n\n/** Default breakpoints: one column on a phone, three on a desktop. */\nconst DEFAULT_COLUMNS: Record<number, number> = { 0: 1, 640: 2, 1024: 3 };\n\n/**\n * Masonry layout: cards of uneven height packed into columns with an even bottom\n * edge.\n *\n * Measures the rendered cards and deals each one into the shortest column, rather\n * than using CSS `columns` or `grid-auto-flow: dense`. Both of those are one line\n * of CSS and neither does this job: CSS `columns` breaks a card across the column\n * boundary, and a dense grid keeps every row the height of its tallest cell, which\n * is the ragged bottom edge people reach for masonry to avoid.\n *\n * @example\n * <Masonry items={fotos} itemKey={(foto) => foto.id} columns={{ 0: 1, 700: 2, 1100: 4 }}>\n * {(foto) => <img src={foto.url} alt={foto.alt} />}\n * </Masonry>\n */\nexport function Masonry<T>({\n items,\n children,\n itemKey,\n columns = DEFAULT_COLUMNS,\n gap,\n className,\n style,\n ...rest\n}: MasonryProps<T>) {\n const wrapper = useRef<HTMLDivElement | null>(null);\n const cells = useRef<Map<number, HTMLElement>>(new Map());\n const [width, setWidth] = useState(0);\n const [heights, setHeights] = useState<number[]>([]);\n\n const columnCount = columnsFor(width, columns);\n\n /**\n * Track the container width, not the viewport.\n *\n * A masonry inside a drawer or a two-column page is narrower than the window,\n * and a media query would give it desktop columns at 300px wide. `ResizeObserver`\n * is what makes the breakpoint map mean \"this container\", which is the only\n * useful reading.\n */\n useEffect(() => {\n const node = wrapper.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(([entry]) => {\n setWidth(entry.contentRect.width);\n });\n observer.observe(node);\n setWidth(node.getBoundingClientRect().width);\n return () => observer.disconnect();\n }, []);\n\n /**\n * Re-read every card's height, and store it only when something moved.\n *\n * The equality check is what stops the loop: writing a new array on every pass\n * would re-render, re-measure and write again. The first render lays out in\n * source order with weight 1 each — never blank — and this pass re-deals with\n * real numbers.\n */\n const measure = useCallback(() => {\n const next = items.map((_, index) => {\n const node = cells.current.get(index);\n return node ? node.getBoundingClientRect().height : 1;\n });\n setHeights((current) =>\n current.length === next.length && current.every((value, i) => value === next[i])\n ? current\n : next,\n );\n }, [items]);\n\n useLayoutEffect(measure, [measure, columnCount, width]);\n\n /**\n * Re-measure when a card changes size on its own — an image finishing its\n * download is the common one, and it is exactly the case a height measured at\n * mount gets wrong.\n */\n useEffect(() => {\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n for (const node of cells.current.values()) observer.observe(node);\n return () => observer.disconnect();\n }, [measure, columnCount]);\n\n const layout = useMemo(\n () =>\n distribute(\n items.map((_, index) => heights[index] ?? 1),\n columnCount,\n ),\n [items, heights, columnCount],\n );\n\n return (\n <div\n ref={wrapper}\n className={cn(styles.wrapper, className)}\n style={{ ...style, ...(gap ? { \"--tempest-masonry-gap\": gap } : {}) }}\n {...rest}\n >\n {layout.map((columnItems, column) => (\n <div key={column} className={styles.column}>\n {columnItems.map((index) => {\n const item = items[index];\n return (\n <div\n key={itemKey ? itemKey(item, index) : index}\n className={styles.item}\n ref={(node) => {\n if (node) cells.current.set(index, node);\n else cells.current.delete(index);\n }}\n >\n {children(item, index)}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":";;;;;;AAoCA,IAAM,IAA0C;CAAE,GAAG;CAAG,KAAK;CAAG,MAAM;AAAE;AAiBxE,SAAgB,EAAW,EACvB,UACA,aACA,YACA,aAAU,GACV,QACA,cACA,UACA,GAAG,KACa;CAChB,IAAM,IAAU,EAA8B,IAAI,GAC5C,IAAQ,kBAAiC,IAAI,IAAI,CAAC,GAClD,CAAC,GAAO,KAAY,EAAS,CAAC,GAC9B,CAAC,GAAS,KAAc,EAAmB,CAAC,CAAC,GAE7C,IAAc,EAAW,GAAO,CAAO;CAU7C,QAAgB;EACZ,IAAM,IAAO,EAAQ;EACrB,IAAI,CAAC,KAAQ,OAAO,iBAAmB,KAAa;EACpD,IAAM,IAAW,IAAI,gBAAgB,CAAC,OAAW;GAC7C,EAAS,EAAM,YAAY,KAAK;EACpC,CAAC;EAGD,OAFA,EAAS,QAAQ,CAAI,GACrB,EAAS,EAAK,sBAAsB,CAAC,CAAC,KAAK,SAC9B,EAAS,WAAW;CACrC,GAAG,CAAC,CAAC;CAUL,IAAM,IAAU,QAAkB;EAC9B,IAAM,IAAO,EAAM,KAAK,GAAG,MAAU;GACjC,IAAM,IAAO,EAAM,QAAQ,IAAI,CAAK;GACpC,OAAO,IAAO,EAAK,sBAAsB,CAAC,CAAC,SAAS;EACxD,CAAC;EACD,GAAY,MACR,EAAQ,WAAW,EAAK,UAAU,EAAQ,OAAO,GAAO,MAAM,MAAU,EAAK,EAAE,IACzE,IACA,CACV;CACJ,GAAG,CAAC,CAAK,CAAC;CASV,AAPA,EAAgB,GAAS;EAAC;EAAS;EAAa;CAAK,CAAC,GAOtD,QAAgB;EACZ,IAAI,OAAO,iBAAmB,KAAa;EAC3C,IAAM,IAAW,IAAI,eAAe,CAAO;EAC3C,KAAK,IAAM,KAAQ,EAAM,QAAQ,OAAO,GAAG,EAAS,QAAQ,CAAI;EAChE,aAAa,EAAS,WAAW;CACrC,GAAG,CAAC,GAAS,CAAW,CAAC;CAEzB,IAAM,IAAS,QAEP,EACI,EAAM,KAAK,GAAG,MAAU,EAAQ,MAAU,CAAC,GAC3C,CACJ,GACJ;EAAC;EAAO;EAAS;CAAW,CAChC;CAEA,OACI,kBAAC,OAAD;EACI,KAAK;EACL,WAAW,EAAG,EAAO,SAAS,CAAS;EACvC,OAAO;GAAE,GAAG;GAAO,GAAI,IAAM,EAAE,yBAAyB,EAAI,IAAI,CAAC;EAAG;EACpE,GAAI;EAEH,UAAA,EAAO,KAAK,GAAa,MACtB,kBAAC,OAAD;GAAkB,WAAW,EAAO;GAC/B,UAAA,EAAY,KAAK,MAAU;IACxB,IAAM,IAAO,EAAM;IACnB,OACI,kBAAC,OAAD;KAEI,WAAW,EAAO;KAClB,MAAM,MAAS;MACX,AAAI,IAAM,EAAM,QAAQ,IAAI,GAAO,CAAI,IAClC,EAAM,QAAQ,OAAO,CAAK;KACnC;KAEC,UAAA,EAAS,GAAM,CAAK;IACpB,GARI,IAAU,EAAQ,GAAM,CAAK,IAAI,CAQrC;GAEb,CAAC;EACA,GAhBK,CAgBL,CACR;CACA,CAAA;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"Menubar.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n}\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n triggerRefs.current[openIndex]?.focus();\n return;\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.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 }, [openIndex, menus.length, close]);\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>\n {item.label}\n </span>\n {item.shortcut && (\n <span className={styles.shortcut}>\n {item.shortcut}\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Menubar.cjs","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n}\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n triggerRefs.current[openIndex]?.focus();\n return;\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.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 }, [openIndex, menus.length, close]);\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>\n {item.label}\n </span>\n {item.shortcut && (\n <span className={styles.shortcut}>\n {item.shortcut}\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Menubar.js","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n}\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n triggerRefs.current[openIndex]?.focus();\n return;\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.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 }, [openIndex, menus.length, close]);\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>\n {item.label}\n </span>\n {item.shortcut && (\n <span className={styles.shortcut}>\n {item.shortcut}\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AAmCA,SAAS,EAAY,GAAgD;CACjE,OAAO,eAAe;AAC1B;AAwBA,SAAgB,EAAQ,EAAE,UAAO,cAAW,GAAG,KAAuB;CAClE,IAAM,CAAC,GAAW,KAAgB,EAAiB,EAAE,GAC/C,IAAS,EAAM,GACf,IAAU,EAAuB,IAAI,GACrC,IAAc,EAAwC,CAAC,CAAC,GAExD,IAAQ,QAAwB;EAClC,EAAa,EAAE;CACnB,GAAG,CAAC,CAAC;CAkCL,OAhCA,QAAgB;EACZ,IAAI,MAAc,IAAI;EACtB,IAAM,KAAS,MAA+B;GAC1C,IAAI,EAAM,QAAQ,UAAU;IAExB,AADA,EAAM,GACN,EAAY,QAAQ,EAAU,EAAE,MAAM;IACtC;GACJ;GACA,IAAI,EAAM,QAAQ,cAAc;IAC5B,EAAM,eAAe;IACrB,IAAM,KAAQ,IAAY,KAAK,EAAM;IAErC,AADA,EAAa,CAAI,GACjB,EAAY,QAAQ,EAAK,EAAE,MAAM;GACrC;GACA,IAAI,EAAM,QAAQ,aAAa;IAC3B,EAAM,eAAe;IACrB,IAAM,KAAQ,IAAY,IAAI,EAAM,UAAU,EAAM;IAEpD,AADA,EAAa,CAAI,GACjB,EAAY,QAAQ,EAAK,EAAE,MAAM;GACrC;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;EAAW,EAAM;EAAQ;CAAK,CAAC,GAG/B,kBAAC,OAAD;EAAK,KAAK;EAAS,MAAK;EAAU,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;YACxE,EAAM,KAAK,GAAM,MAAU;GACxB,IAAM,IAAO,MAAc,GACrB,IAAU,GAAG,EAAO,SAAS;GACnC,OACI,kBAAC,OAAD;IAAiB,WAAW,EAAO;cAAnC,CACI,kBAAC,UAAD;KACI,MAAM,MAAO;MACT,EAAY,QAAQ,KAAS;KACjC;KACA,MAAK;KACL,MAAK;KACL,iBAAc;KACd,iBAAe;KACf,iBAAe,IAAO,IAAU,KAAA;KAChC,WAAW,EAAG,EAAO,SAAS,KAAQ,EAAO,WAAW;KACxD,eAAe,GAAc,MAAU,MAAS,IAAQ,KAAK,CAAM;eAElE,EAAK;IACF,CAAA,GACP,KACG,kBAAC,MAAD;KAAI,IAAI;KAAS,MAAK;KAAO,WAAW,EAAO;eAC1C,EAAK,MAAM,KAAK,GAAM,MACf,EAAY,CAAI,IAEZ,kBAAC,MAAD;MAEI,MAAK;MACL,WAAW,EAAO;MAClB,eAAA;KACH,GAJQ,CAIR,IAIL,kBAAC,MAAD;MAAoB,MAAK;gBACrB,kBAAC,UAAD;OACI,MAAK;OACL,MAAK;OACL,WAAW,EAAO;OAClB,UAAU,EAAK;OACf,eAAe;QAEX,AADA,EAAK,WAAW,GAChB,EAAM;OACV;iBARJ,CAUI,kBAAC,QAAD;QAAM,WAAW,EAAO;kBACnB,EAAK;OACJ,CAAA,GACL,EAAK,YACF,kBAAC,QAAD;QAAM,WAAW,EAAO;kBACnB,EAAK;OACJ,CAAA,CAEN;;KACR,GApBK,CAoBL,CAEX;IACD,CAAA,CAEP;MAtDK,CAsDL;EAEb,CAAC;CACA,CAAA;AAEb"}
1
+ {"version":3,"file":"Menubar.js","names":[],"sources":["../../../src/components/Menubar/Menubar.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — a menubar has two keyboard planes — arrow keys\n * move between menus, arrows inside one move between items — and the body owns both\n * because a key press is routed by which plane is open.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Menubar.module.css\";\n\nexport type MenubarItem =\n | {\n /** Visible label for the action. */\n label: ReactNode;\n /** Invoked when the action is selected. */\n onSelect?: () => void;\n /** Disable the action. */\n disabled?: boolean;\n /** Optional shortcut hint rendered right-aligned (e.g. `\"⌘S\"`). */\n shortcut?: string;\n }\n | { separator: true };\n\nexport interface MenubarMenu {\n /** Top-level menu label (e.g. `\"File\"`). */\n label: ReactNode;\n /** Items shown in the menu's dropdown. */\n items: MenubarItem[];\n}\n\nexport interface MenubarProps extends HTMLAttributes<HTMLDivElement> {\n /** Top-level menus rendered left-to-right. */\n menus: MenubarMenu[];\n}\n\nfunction isSeparator(item: MenubarItem): item is { separator: true } {\n return \"separator\" in item;\n}\n\n/**\n * Application menubar (File / Edit-style).\n *\n * - `role=\"menubar\"`; each menu is a button that opens a dropdown.\n * - Arrow Left/Right moves between menus (wrapping); opening one closes others.\n * - Closes on outside click, Escape, or selecting an item.\n * - Items may carry a `shortcut` (right-aligned) or be a `{ separator: true }`.\n *\n * @example\n * <Menubar\n * menus={[\n * {\n * label: \"File\",\n * items: [\n * { label: \"New\", shortcut: \"⌘N\", onSelect: () => create() },\n * { separator: true },\n * { label: \"Quit\", onSelect: () => quit() },\n * ],\n * },\n * ]}\n * />\n */\nexport function Menubar({ menus, className, ...props }: MenubarProps) {\n const [openIndex, setOpenIndex] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const triggerRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const close = useCallback((): void => {\n setOpenIndex(-1);\n }, []);\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n triggerRefs.current[openIndex]?.focus();\n return;\n }\n if (event.key === \"ArrowRight\") {\n event.preventDefault();\n const next = (openIndex + 1) % menus.length;\n setOpenIndex(next);\n triggerRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n const prev = (openIndex - 1 + menus.length) % menus.length;\n setOpenIndex(prev);\n triggerRefs.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 }, [openIndex, menus.length, close]);\n\n return (\n <div ref={rootRef} role=\"menubar\" className={cn(styles.root, className)} {...props}>\n {menus.map((menu, index) => {\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <div key={index} className={styles.menu}>\n <button\n ref={(el) => {\n triggerRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={open ? panelId : undefined}\n className={cn(styles.trigger, open && styles.triggerOpen)}\n onClick={() => setOpenIndex((prev) => (prev === index ? -1 : index))}\n >\n {menu.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {menu.items.map((item, itemIndex) => {\n if (isSeparator(item)) {\n return (\n <li\n key={itemIndex}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n return (\n <li key={itemIndex} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitem\"\n className={styles.item}\n disabled={item.disabled}\n onClick={() => {\n item.onSelect?.();\n close();\n }}\n >\n <span className={styles.itemLabel}>\n {item.label}\n </span>\n {item.shortcut && (\n <span className={styles.shortcut}>\n {item.shortcut}\n </span>\n )}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AAmCA,SAAS,EAAY,GAAgD;CACjE,OAAO,eAAe;AAC1B;AAwBA,SAAgB,EAAQ,EAAE,UAAO,cAAW,GAAG,KAAuB;CAClE,IAAM,CAAC,GAAW,KAAgB,EAAiB,EAAE,GAC/C,IAAS,EAAM,GACf,IAAU,EAAuB,IAAI,GACrC,IAAc,EAAwC,CAAC,CAAC,GAExD,IAAQ,QAAwB;EAClC,EAAa,EAAE;CACnB,GAAG,CAAC,CAAC;CAkCL,OAhCA,QAAgB;EACZ,IAAI,MAAc,IAAI;EACtB,IAAM,KAAS,MAA+B;GAC1C,IAAI,EAAM,QAAQ,UAAU;IAExB,AADA,EAAM,GACN,EAAY,QAAQ,EAAU,EAAE,MAAM;IACtC;GACJ;GACA,IAAI,EAAM,QAAQ,cAAc;IAC5B,EAAM,eAAe;IACrB,IAAM,KAAQ,IAAY,KAAK,EAAM;IAErC,AADA,EAAa,CAAI,GACjB,EAAY,QAAQ,EAAK,EAAE,MAAM;GACrC;GACA,IAAI,EAAM,QAAQ,aAAa;IAC3B,EAAM,eAAe;IACrB,IAAM,KAAQ,IAAY,IAAI,EAAM,UAAU,EAAM;IAEpD,AADA,EAAa,CAAI,GACjB,EAAY,QAAQ,EAAK,EAAE,MAAM;GACrC;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;EAAW,EAAM;EAAQ;CAAK,CAAC,GAG/B,kBAAC,OAAD;EAAK,KAAK;EAAS,MAAK;EAAU,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;EACxE,UAAA,EAAM,KAAK,GAAM,MAAU;GACxB,IAAM,IAAO,MAAc,GACrB,IAAU,GAAG,EAAO,SAAS;GACnC,OACI,kBAAC,OAAD;IAAiB,WAAW,EAAO;IAAnC,UAAA,CACI,kBAAC,UAAD;KACI,MAAM,MAAO;MACT,EAAY,QAAQ,KAAS;KACjC;KACA,MAAK;KACL,MAAK;KACL,iBAAc;KACd,iBAAe;KACf,iBAAe,IAAO,IAAU,KAAA;KAChC,WAAW,EAAG,EAAO,SAAS,KAAQ,EAAO,WAAW;KACxD,eAAe,GAAc,MAAU,MAAS,IAAQ,KAAK,CAAM;KAElE,UAAA,EAAK;IACF,CAAA,GACP,KACG,kBAAC,MAAD;KAAI,IAAI;KAAS,MAAK;KAAO,WAAW,EAAO;KAC1C,UAAA,EAAK,MAAM,KAAK,GAAM,MACf,EAAY,CAAI,IAEZ,kBAAC,MAAD;MAEI,MAAK;MACL,WAAW,EAAO;MAClB,eAAA;KACH,GAJQ,CAIR,IAIL,kBAAC,MAAD;MAAoB,MAAK;MACrB,UAAA,kBAAC,UAAD;OACI,MAAK;OACL,MAAK;OACL,WAAW,EAAO;OAClB,UAAU,EAAK;OACf,eAAe;QAEX,AADA,EAAK,WAAW,GAChB,EAAM;OACV;OARJ,UAAA,CAUI,kBAAC,QAAD;QAAM,WAAW,EAAO;QACnB,UAAA,EAAK;OACJ,CAAA,GACL,EAAK,YACF,kBAAC,QAAD;QAAM,WAAW,EAAO;QACnB,UAAA,EAAK;OACJ,CAAA,CAEN;;KACR,GApBK,CAoBL,CAEX;IACD,CAAA,CAEP;GAtDK,GAAA,CAsDL;EAEb,CAAC;CACA,CAAA;AAEb"}
@@ -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,EAAA,EAAA,MAAA,CAAgB,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,EAAA,EAAA,aAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,KAAK,eACL,YAAe,CACP,GAAiB,EAAQ,CACjC,YAEA,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,WAZ9C,EAcM,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,gBAA1B,CACK,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,UAAW,EAAA,QAAO,eAC9B,CACD,CAAA,EAEP,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,MAClB,aAAW,SACX,QAAS,YAET,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,gBAAS,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,iBACjD,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\";\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 +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;YAEA,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;aAZ9C;KAcM,KAAS,CAAC,MACR,kBAAC,UAAD;KAAQ,WAAW,EAAO;eAA1B,CACK,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,WAAW,EAAO;gBAC9B;KACD,CAAA,GAEP,CAAC,KACE,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,cAAW;MACX,SAAS;gBAET,kBAAC,GAAD,CAAY,CAAA;KACR,CAAA,CAER;;IAEZ,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAO;IAAc,CAAA;IAC3C,KAAU,kBAAC,UAAD;KAAQ,WAAW,EAAO;eAAS;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;YACjD,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\";\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 +1 @@
1
- {"version":3,"file":"ModalsManager.cjs","names":[],"sources":["../../../src/components/ModalsManager/ModalsManager.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the imperative API (open, close, confirm, prompt) and\n * the stack that renders it live in one file so the returned promise and the mounted\n * element cannot drift apart.\n */\nimport { createContext, useCallback, useContext, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ConfirmDialog } from \"@/components/ConfirmDialog\";\nimport { Modal } from \"@/components/Modal\";\nimport type { ModalSize } from \"@/components/Modal\";\nimport styles from \"./ModalsManager.module.css\";\n\n/** Options accepted by {@link ModalsApi.open}. */\nexport interface OpenModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Modal body content. */\n children: ReactNode;\n /** Dialog size. Default `md`. */\n size?: ModalSize;\n /** Allow closing by clicking the backdrop. Default `true`. */\n closeOnBackdrop?: boolean;\n /** Allow closing with the Esc key. Default `true`. */\n closeOnEsc?: boolean;\n /** Hide the header close button. */\n hideCloseButton?: boolean;\n /** Called after the modal is removed from the stack. */\n onClose?: () => void;\n}\n\n/** Options accepted by {@link ModalsApi.confirm}. */\nexport interface ConfirmModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Prompt message rendered in the dialog body. */\n message: ReactNode;\n /** Confirm button label. Default `Confirmar`. */\n confirmLabel?: string;\n /** Cancel button label. Default `Cancelar`. */\n cancelLabel?: string;\n /** Render the confirm button in the danger variant. */\n danger?: boolean;\n /** Called when the user confirms. */\n onConfirm?: () => void | Promise<void>;\n /** Called when the user cancels or dismisses. */\n onCancel?: () => void;\n}\n\n/** Imperative API returned by {@link useModals}. */\nexport interface ModalsApi {\n /** Push a content modal. Returns its stack id. */\n open: (options: OpenModalOptions) => string;\n /** Push a confirmation dialog. Returns its stack id. */\n confirm: (options: ConfirmModalOptions) => string;\n /** Remove the modal with the given id. */\n close: (id: string) => void;\n /** Remove every modal from the stack. */\n closeAll: () => void;\n}\n\ninterface ContentEntry {\n kind: \"content\";\n id: string;\n options: OpenModalOptions;\n}\n\ninterface ConfirmEntry {\n kind: \"confirm\";\n id: string;\n options: ConfirmModalOptions;\n}\n\ntype StackEntry = ContentEntry | ConfirmEntry;\n\nconst ModalsContext = createContext<ModalsApi | null>(null);\n\n/** Props for {@link ModalsProvider}. */\nexport interface ModalsProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides imperative modal control via {@link useModals} and renders the open\n * modal stack. Mount once near the app root.\n *\n * @example\n * <ModalsProvider>\n * <App />\n * </ModalsProvider>\n */\nexport function ModalsProvider({ children }: ModalsProviderProps) {\n const [stack, setStack] = useState<StackEntry[]>([]);\n const counter = useRef(0);\n\n const nextId = useCallback((): string => {\n counter.current += 1;\n return `modal-${counter.current}`;\n }, []);\n\n const close = useCallback((id: string): void => {\n setStack((current) => current.filter((entry) => entry.id !== id));\n }, []);\n\n const closeAll = useCallback((): void => {\n setStack([]);\n }, []);\n\n const open = useCallback(\n (options: OpenModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"content\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const confirm = useCallback(\n (options: ConfirmModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"confirm\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const api = useMemo<ModalsApi>(\n () => ({ open, confirm, close, closeAll }),\n [open, confirm, close, closeAll],\n );\n\n return (\n <ModalsContext.Provider value={api}>\n {children}\n <div className={styles.stack}>\n {stack.map((entry) =>\n entry.kind === \"content\" ? (\n <ContentModal key={entry.id} entry={entry} close={close} />\n ) : (\n <ConfirmModal key={entry.id} entry={entry} close={close} />\n ),\n )}\n </div>\n </ModalsContext.Provider>\n );\n}\n\ninterface ContentModalProps {\n entry: ContentEntry;\n close: (id: string) => void;\n}\n\nfunction ContentModal({ entry, close }: ContentModalProps) {\n const { options, id } = entry;\n const handleClose = (): void => {\n options.onClose?.();\n close(id);\n };\n return (\n <Modal\n open\n onClose={handleClose}\n title={options.title}\n size={options.size}\n closeOnBackdrop={options.closeOnBackdrop}\n closeOnEsc={options.closeOnEsc}\n hideCloseButton={options.hideCloseButton}\n >\n {options.children}\n </Modal>\n );\n}\n\ninterface ConfirmModalProps {\n entry: ConfirmEntry;\n close: (id: string) => void;\n}\n\nfunction ConfirmModal({ entry, close }: ConfirmModalProps) {\n const { options, id } = entry;\n const [loading, setLoading] = useState(false);\n\n const handleConfirm = async (): Promise<void> => {\n try {\n setLoading(true);\n await options.onConfirm?.();\n } finally {\n setLoading(false);\n close(id);\n }\n };\n\n const handleCancel = (): void => {\n options.onCancel?.();\n close(id);\n };\n\n return (\n <ConfirmDialog\n open\n title={options.title ?? \"\"}\n description={options.message}\n confirmLabel={options.confirmLabel}\n cancelLabel={options.cancelLabel}\n variant={options.danger ? \"danger\" : \"primary\"}\n loading={loading}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n );\n}\n\n/**\n * Access the imperative modals API. Must be used within a {@link ModalsProvider}.\n *\n * @example\n * const modals = useModals();\n * modals.confirm({ message: \"Excluir item?\", danger: true, onConfirm: del });\n *\n * @throws Error when called outside a {@link ModalsProvider}.\n */\nexport function useModals(): ModalsApi {\n const api = useContext(ModalsContext);\n if (api === null) {\n throw new Error(\"useModals must be used within a <ModalsProvider>\");\n }\n return api;\n}\n"],"mappings":"oLA0EA,IAAM,GAAA,EAAA,EAAA,cAAA,CAAgD,IAAI,EAgB1D,SAAgB,EAAe,CAAE,YAAiC,CAC9D,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAmC,CAAC,CAAC,EAC7C,GAAA,EAAA,EAAA,OAAA,CAAiB,CAAC,EAElB,GAAA,EAAA,EAAA,YAAA,MACF,EAAQ,SAAW,EACZ,SAAS,EAAQ,WACzB,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CAAqB,GAAqB,CAC5C,EAAU,GAAY,EAAQ,OAAQ,GAAU,EAAM,KAAO,CAAE,CAAC,CACpE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,KAAmC,CACrC,EAAS,CAAC,CAAC,CACf,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,YAAA,CACD,GAAsC,CACnC,IAAM,EAAK,EAAO,EAElB,OADA,EAAU,GAAY,CAAC,GAAG,EAAS,CAAE,KAAM,UAAW,KAAI,SAAQ,CAAC,CAAC,EAC7D,CACX,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAA,EAAA,YAAA,CACD,GAAyC,CACtC,IAAM,EAAK,EAAO,EAElB,OADA,EAAU,GAAY,CAAC,GAAG,EAAS,CAAE,KAAM,UAAW,KAAI,SAAQ,CAAC,CAAC,EAC7D,CACX,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAA,EAAA,QAAA,MACK,CAAE,OAAM,UAAS,QAAO,UAAS,GACxC,CAAC,EAAM,EAAS,EAAO,CAAQ,CACnC,EAEA,OACI,EAAA,EAAA,KAAA,CAAC,EAAc,SAAf,CAAwB,MAAO,WAA/B,CACK,GACD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,eAClB,EAAM,IAAK,GACR,EAAM,OAAS,WACX,EAAA,EAAA,IAAA,CAAC,EAAD,CAAoC,QAAc,OAAQ,EAAvC,EAAM,EAAiC,GAE1D,EAAA,EAAA,IAAA,CAAC,EAAD,CAAoC,QAAc,OAAQ,EAAvC,EAAM,EAAiC,CAElE,CACC,CAAA,CACe,GAEhC,CAOA,SAAS,EAAa,CAAE,QAAO,SAA4B,CACvD,GAAM,CAAE,UAAS,MAAO,EAKxB,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,KAAA,GACA,YAPwB,CAC5B,EAAQ,UAAU,EAClB,EAAM,CAAE,CACZ,EAKQ,MAAO,EAAQ,MACf,KAAM,EAAQ,KACd,gBAAiB,EAAQ,gBACzB,WAAY,EAAQ,WACpB,gBAAiB,EAAQ,yBAExB,EAAQ,QACN,CAAA,CAEf,CAOA,SAAS,EAAa,CAAE,QAAO,SAA4B,CACvD,GAAM,CAAE,UAAS,MAAO,EAClB,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAAuB,EAAK,EAiB5C,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,cAAD,CACI,KAAA,GACA,MAAO,EAAQ,OAAS,GACxB,YAAa,EAAQ,QACrB,aAAc,EAAQ,aACtB,YAAa,EAAQ,YACrB,QAAS,EAAQ,OAAS,SAAW,UAC5B,UACT,UAAW,SAxB8B,CAC7C,GAAI,CACA,EAAW,EAAI,EACf,MAAM,EAAQ,YAAY,CAC9B,QAAU,CACN,EAAW,EAAK,EAChB,EAAM,CAAE,CACZ,CACJ,EAiBQ,aAfyB,CAC7B,EAAQ,WAAW,EACnB,EAAM,CAAE,CACZ,CAaK,CAAA,CAET,CAWA,SAAgB,GAAuB,CACnC,IAAM,GAAA,EAAA,EAAA,WAAA,CAAiB,CAAa,EACpC,GAAI,IAAQ,KACR,MAAU,MAAM,kDAAkD,EAEtE,OAAO,CACX"}
1
+ {"version":3,"file":"ModalsManager.cjs","names":[],"sources":["../../../src/components/ModalsManager/ModalsManager.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the imperative API (open, close, confirm, prompt) and\n * the stack that renders it live in one file so the returned promise and the mounted\n * element cannot drift apart.\n */\nimport { createContext, useCallback, useContext, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ConfirmDialog } from \"@/components/ConfirmDialog\";\nimport { Modal } from \"@/components/Modal\";\nimport type { ModalSize } from \"@/components/Modal\";\nimport styles from \"./ModalsManager.module.css\";\n\n/** Options accepted by {@link ModalsApi.open}. */\nexport interface OpenModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Modal body content. */\n children: ReactNode;\n /** Dialog size. Default `md`. */\n size?: ModalSize;\n /** Allow closing by clicking the backdrop. Default `true`. */\n closeOnBackdrop?: boolean;\n /** Allow closing with the Esc key. Default `true`. */\n closeOnEsc?: boolean;\n /** Hide the header close button. */\n hideCloseButton?: boolean;\n /** Called after the modal is removed from the stack. */\n onClose?: () => void;\n}\n\n/** Options accepted by {@link ModalsApi.confirm}. */\nexport interface ConfirmModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Prompt message rendered in the dialog body. */\n message: ReactNode;\n /** Confirm button label. Default `Confirmar`. */\n confirmLabel?: string;\n /** Cancel button label. Default `Cancelar`. */\n cancelLabel?: string;\n /** Render the confirm button in the danger variant. */\n danger?: boolean;\n /** Called when the user confirms. */\n onConfirm?: () => void | Promise<void>;\n /** Called when the user cancels or dismisses. */\n onCancel?: () => void;\n}\n\n/** Imperative API returned by {@link useModals}. */\nexport interface ModalsApi {\n /** Push a content modal. Returns its stack id. */\n open: (options: OpenModalOptions) => string;\n /** Push a confirmation dialog. Returns its stack id. */\n confirm: (options: ConfirmModalOptions) => string;\n /** Remove the modal with the given id. */\n close: (id: string) => void;\n /** Remove every modal from the stack. */\n closeAll: () => void;\n}\n\ninterface ContentEntry {\n kind: \"content\";\n id: string;\n options: OpenModalOptions;\n}\n\ninterface ConfirmEntry {\n kind: \"confirm\";\n id: string;\n options: ConfirmModalOptions;\n}\n\ntype StackEntry = ContentEntry | ConfirmEntry;\n\nconst ModalsContext = createContext<ModalsApi | null>(null);\n\n/** Props for {@link ModalsProvider}. */\nexport interface ModalsProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides imperative modal control via {@link useModals} and renders the open\n * modal stack. Mount once near the app root.\n *\n * @example\n * <ModalsProvider>\n * <App />\n * </ModalsProvider>\n */\nexport function ModalsProvider({ children }: ModalsProviderProps) {\n const [stack, setStack] = useState<StackEntry[]>([]);\n const counter = useRef(0);\n\n const nextId = useCallback((): string => {\n counter.current += 1;\n return `modal-${counter.current}`;\n }, []);\n\n const close = useCallback((id: string): void => {\n setStack((current) => current.filter((entry) => entry.id !== id));\n }, []);\n\n const closeAll = useCallback((): void => {\n setStack([]);\n }, []);\n\n const open = useCallback(\n (options: OpenModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"content\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const confirm = useCallback(\n (options: ConfirmModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"confirm\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const api = useMemo<ModalsApi>(\n () => ({ open, confirm, close, closeAll }),\n [open, confirm, close, closeAll],\n );\n\n return (\n <ModalsContext.Provider value={api}>\n {children}\n <div className={styles.stack}>\n {stack.map((entry) =>\n entry.kind === \"content\" ? (\n <ContentModal key={entry.id} entry={entry} close={close} />\n ) : (\n <ConfirmModal key={entry.id} entry={entry} close={close} />\n ),\n )}\n </div>\n </ModalsContext.Provider>\n );\n}\n\ninterface ContentModalProps {\n entry: ContentEntry;\n close: (id: string) => void;\n}\n\nfunction ContentModal({ entry, close }: ContentModalProps) {\n const { options, id } = entry;\n const handleClose = (): void => {\n options.onClose?.();\n close(id);\n };\n return (\n <Modal\n open\n onClose={handleClose}\n title={options.title}\n size={options.size}\n closeOnBackdrop={options.closeOnBackdrop}\n closeOnEsc={options.closeOnEsc}\n hideCloseButton={options.hideCloseButton}\n >\n {options.children}\n </Modal>\n );\n}\n\ninterface ConfirmModalProps {\n entry: ConfirmEntry;\n close: (id: string) => void;\n}\n\nfunction ConfirmModal({ entry, close }: ConfirmModalProps) {\n const { options, id } = entry;\n const [loading, setLoading] = useState(false);\n\n const handleConfirm = async (): Promise<void> => {\n try {\n setLoading(true);\n await options.onConfirm?.();\n } finally {\n setLoading(false);\n close(id);\n }\n };\n\n const handleCancel = (): void => {\n options.onCancel?.();\n close(id);\n };\n\n return (\n <ConfirmDialog\n open\n title={options.title ?? \"\"}\n description={options.message}\n confirmLabel={options.confirmLabel}\n cancelLabel={options.cancelLabel}\n variant={options.danger ? \"danger\" : \"primary\"}\n loading={loading}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n );\n}\n\n/**\n * Access the imperative modals API. Must be used within a {@link ModalsProvider}.\n *\n * @example\n * const modals = useModals();\n * modals.confirm({ message: \"Excluir item?\", danger: true, onConfirm: del });\n *\n * @throws Error when called outside a {@link ModalsProvider}.\n */\nexport function useModals(): ModalsApi {\n const api = useContext(ModalsContext);\n if (api === null) {\n throw new Error(\"useModals must be used within a <ModalsProvider>\");\n }\n return api;\n}\n"],"mappings":"oLA0EA,IAAM,GAAA,EAAgB,EAAA,cAAA,CAAgC,IAAI,EAgB1D,SAAgB,EAAe,CAAE,YAAiC,CAC9D,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAuB,CAAC,CAAC,EAC7C,GAAA,EAAU,EAAA,OAAA,CAAO,CAAC,EAElB,GAAA,EAAS,EAAA,YAAA,MACX,EAAQ,SAAW,EACZ,SAAS,EAAQ,WACzB,CAAC,CAAC,EAEC,GAAA,EAAQ,EAAA,YAAA,CAAa,GAAqB,CAC5C,EAAU,GAAY,EAAQ,OAAQ,GAAU,EAAM,KAAO,CAAE,CAAC,CACpE,EAAG,CAAC,CAAC,EAEC,GAAA,EAAW,EAAA,YAAA,KAAwB,CACrC,EAAS,CAAC,CAAC,CACf,EAAG,CAAC,CAAC,EAEC,GAAA,EAAO,EAAA,YAAA,CACR,GAAsC,CACnC,IAAM,EAAK,EAAO,EAElB,OADA,EAAU,GAAY,CAAC,GAAG,EAAS,CAAE,KAAM,UAAW,KAAI,SAAQ,CAAC,CAAC,EAC7D,CACX,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAU,EAAA,YAAA,CACX,GAAyC,CACtC,IAAM,EAAK,EAAO,EAElB,OADA,EAAU,GAAY,CAAC,GAAG,EAAS,CAAE,KAAM,UAAW,KAAI,SAAQ,CAAC,CAAC,EAC7D,CACX,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAM,EAAA,QAAA,MACD,CAAE,OAAM,UAAS,QAAO,UAAS,GACxC,CAAC,EAAM,EAAS,EAAO,CAAQ,CACnC,EAEA,OACI,EAAA,EAAA,KAAA,CAAC,EAAc,SAAf,CAAwB,MAAO,EAA/B,SAAA,CACK,GACD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,MAClB,SAAA,EAAM,IAAK,GACR,EAAM,OAAS,WACX,EAAA,EAAA,IAAA,CAAC,EAAD,CAAoC,QAAc,OAAQ,EAAvC,EAAM,EAAiC,GAE1D,EAAA,EAAA,IAAA,CAAC,EAAD,CAAoC,QAAc,OAAQ,EAAvC,EAAM,EAAiC,CAElE,CACC,CAAA,CACe,GAEhC,CAOA,SAAS,EAAa,CAAE,QAAO,SAA4B,CACvD,GAAM,CAAE,UAAS,MAAO,EAKxB,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,KAAA,GACA,YAPwB,CAC5B,EAAQ,UAAU,EAClB,EAAM,CAAE,CACZ,EAKQ,MAAO,EAAQ,MACf,KAAM,EAAQ,KACd,gBAAiB,EAAQ,gBACzB,WAAY,EAAQ,WACpB,gBAAiB,EAAQ,gBAExB,SAAA,EAAQ,QACN,CAAA,CAEf,CAOA,SAAS,EAAa,CAAE,QAAO,SAA4B,CACvD,GAAM,CAAE,UAAS,MAAO,EAClB,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAS,EAAK,EAiB5C,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,cAAD,CACI,KAAA,GACA,MAAO,EAAQ,OAAS,GACxB,YAAa,EAAQ,QACrB,aAAc,EAAQ,aACtB,YAAa,EAAQ,YACrB,QAAS,EAAQ,OAAS,SAAW,UAC5B,UACT,UAAW,SAxB8B,CAC7C,GAAI,CACA,EAAW,EAAI,EACf,MAAM,EAAQ,YAAY,CAC9B,QAAU,CACN,EAAW,EAAK,EAChB,EAAM,CAAE,CACZ,CACJ,EAiBQ,aAfyB,CAC7B,EAAQ,WAAW,EACnB,EAAM,CAAE,CACZ,CAaK,CAAA,CAET,CAWA,SAAgB,GAAuB,CACnC,IAAM,GAAA,EAAM,EAAA,WAAA,CAAW,CAAa,EACpC,GAAI,IAAQ,KACR,MAAU,MAAM,kDAAkD,EAEtE,OAAO,CACX"}
@@ -1 +1 @@
1
- {"version":3,"file":"ModalsManager.js","names":[],"sources":["../../../src/components/ModalsManager/ModalsManager.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the imperative API (open, close, confirm, prompt) and\n * the stack that renders it live in one file so the returned promise and the mounted\n * element cannot drift apart.\n */\nimport { createContext, useCallback, useContext, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ConfirmDialog } from \"@/components/ConfirmDialog\";\nimport { Modal } from \"@/components/Modal\";\nimport type { ModalSize } from \"@/components/Modal\";\nimport styles from \"./ModalsManager.module.css\";\n\n/** Options accepted by {@link ModalsApi.open}. */\nexport interface OpenModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Modal body content. */\n children: ReactNode;\n /** Dialog size. Default `md`. */\n size?: ModalSize;\n /** Allow closing by clicking the backdrop. Default `true`. */\n closeOnBackdrop?: boolean;\n /** Allow closing with the Esc key. Default `true`. */\n closeOnEsc?: boolean;\n /** Hide the header close button. */\n hideCloseButton?: boolean;\n /** Called after the modal is removed from the stack. */\n onClose?: () => void;\n}\n\n/** Options accepted by {@link ModalsApi.confirm}. */\nexport interface ConfirmModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Prompt message rendered in the dialog body. */\n message: ReactNode;\n /** Confirm button label. Default `Confirmar`. */\n confirmLabel?: string;\n /** Cancel button label. Default `Cancelar`. */\n cancelLabel?: string;\n /** Render the confirm button in the danger variant. */\n danger?: boolean;\n /** Called when the user confirms. */\n onConfirm?: () => void | Promise<void>;\n /** Called when the user cancels or dismisses. */\n onCancel?: () => void;\n}\n\n/** Imperative API returned by {@link useModals}. */\nexport interface ModalsApi {\n /** Push a content modal. Returns its stack id. */\n open: (options: OpenModalOptions) => string;\n /** Push a confirmation dialog. Returns its stack id. */\n confirm: (options: ConfirmModalOptions) => string;\n /** Remove the modal with the given id. */\n close: (id: string) => void;\n /** Remove every modal from the stack. */\n closeAll: () => void;\n}\n\ninterface ContentEntry {\n kind: \"content\";\n id: string;\n options: OpenModalOptions;\n}\n\ninterface ConfirmEntry {\n kind: \"confirm\";\n id: string;\n options: ConfirmModalOptions;\n}\n\ntype StackEntry = ContentEntry | ConfirmEntry;\n\nconst ModalsContext = createContext<ModalsApi | null>(null);\n\n/** Props for {@link ModalsProvider}. */\nexport interface ModalsProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides imperative modal control via {@link useModals} and renders the open\n * modal stack. Mount once near the app root.\n *\n * @example\n * <ModalsProvider>\n * <App />\n * </ModalsProvider>\n */\nexport function ModalsProvider({ children }: ModalsProviderProps) {\n const [stack, setStack] = useState<StackEntry[]>([]);\n const counter = useRef(0);\n\n const nextId = useCallback((): string => {\n counter.current += 1;\n return `modal-${counter.current}`;\n }, []);\n\n const close = useCallback((id: string): void => {\n setStack((current) => current.filter((entry) => entry.id !== id));\n }, []);\n\n const closeAll = useCallback((): void => {\n setStack([]);\n }, []);\n\n const open = useCallback(\n (options: OpenModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"content\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const confirm = useCallback(\n (options: ConfirmModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"confirm\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const api = useMemo<ModalsApi>(\n () => ({ open, confirm, close, closeAll }),\n [open, confirm, close, closeAll],\n );\n\n return (\n <ModalsContext.Provider value={api}>\n {children}\n <div className={styles.stack}>\n {stack.map((entry) =>\n entry.kind === \"content\" ? (\n <ContentModal key={entry.id} entry={entry} close={close} />\n ) : (\n <ConfirmModal key={entry.id} entry={entry} close={close} />\n ),\n )}\n </div>\n </ModalsContext.Provider>\n );\n}\n\ninterface ContentModalProps {\n entry: ContentEntry;\n close: (id: string) => void;\n}\n\nfunction ContentModal({ entry, close }: ContentModalProps) {\n const { options, id } = entry;\n const handleClose = (): void => {\n options.onClose?.();\n close(id);\n };\n return (\n <Modal\n open\n onClose={handleClose}\n title={options.title}\n size={options.size}\n closeOnBackdrop={options.closeOnBackdrop}\n closeOnEsc={options.closeOnEsc}\n hideCloseButton={options.hideCloseButton}\n >\n {options.children}\n </Modal>\n );\n}\n\ninterface ConfirmModalProps {\n entry: ConfirmEntry;\n close: (id: string) => void;\n}\n\nfunction ConfirmModal({ entry, close }: ConfirmModalProps) {\n const { options, id } = entry;\n const [loading, setLoading] = useState(false);\n\n const handleConfirm = async (): Promise<void> => {\n try {\n setLoading(true);\n await options.onConfirm?.();\n } finally {\n setLoading(false);\n close(id);\n }\n };\n\n const handleCancel = (): void => {\n options.onCancel?.();\n close(id);\n };\n\n return (\n <ConfirmDialog\n open\n title={options.title ?? \"\"}\n description={options.message}\n confirmLabel={options.confirmLabel}\n cancelLabel={options.cancelLabel}\n variant={options.danger ? \"danger\" : \"primary\"}\n loading={loading}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n );\n}\n\n/**\n * Access the imperative modals API. Must be used within a {@link ModalsProvider}.\n *\n * @example\n * const modals = useModals();\n * modals.confirm({ message: \"Excluir item?\", danger: true, onConfirm: del });\n *\n * @throws Error when called outside a {@link ModalsProvider}.\n */\nexport function useModals(): ModalsApi {\n const api = useContext(ModalsContext);\n if (api === null) {\n throw new Error(\"useModals must be used within a <ModalsProvider>\");\n }\n return api;\n}\n"],"mappings":";;;;;;AA0EA,IAAM,IAAgB,EAAgC,IAAI;AAgB1D,SAAgB,EAAe,EAAE,eAAiC;CAC9D,IAAM,CAAC,GAAO,KAAY,EAAuB,CAAC,CAAC,GAC7C,IAAU,EAAO,CAAC,GAElB,IAAS,SACX,EAAQ,WAAW,GACZ,SAAS,EAAQ,YACzB,CAAC,CAAC,GAEC,IAAQ,GAAa,MAAqB;EAC5C,GAAU,MAAY,EAAQ,QAAQ,MAAU,EAAM,OAAO,CAAE,CAAC;CACpE,GAAG,CAAC,CAAC,GAEC,IAAW,QAAwB;EACrC,EAAS,CAAC,CAAC;CACf,GAAG,CAAC,CAAC,GAEC,IAAO,GACR,MAAsC;EACnC,IAAM,IAAK,EAAO;EAElB,OADA,GAAU,MAAY,CAAC,GAAG,GAAS;GAAE,MAAM;GAAW;GAAI;EAAQ,CAAC,CAAC,GAC7D;CACX,GACA,CAAC,CAAM,CACX,GAEM,IAAU,GACX,MAAyC;EACtC,IAAM,IAAK,EAAO;EAElB,OADA,GAAU,MAAY,CAAC,GAAG,GAAS;GAAE,MAAM;GAAW;GAAI;EAAQ,CAAC,CAAC,GAC7D;CACX,GACA,CAAC,CAAM,CACX,GAEM,IAAM,SACD;EAAE;EAAM;EAAS;EAAO;CAAS,IACxC;EAAC;EAAM;EAAS;EAAO;CAAQ,CACnC;CAEA,OACI,kBAAC,EAAc,UAAf;EAAwB,OAAO;YAA/B,CACK,GACD,kBAAC,OAAD;GAAK,WAAW,EAAO;aAClB,EAAM,KAAK,MACR,EAAM,SAAS,YACX,kBAAC,GAAD;IAAoC;IAAc;GAAQ,GAAvC,EAAM,EAAiC,IAE1D,kBAAC,GAAD;IAAoC;IAAc;GAAQ,GAAvC,EAAM,EAAiC,CAElE;EACC,CAAA,CACe;;AAEhC;AAOA,SAAS,EAAa,EAAE,UAAO,YAA4B;CACvD,IAAM,EAAE,YAAS,UAAO;CAKxB,OACI,kBAAC,GAAD;EACI,MAAA;EACA,eAPwB;GAE5B,AADA,EAAQ,UAAU,GAClB,EAAM,CAAE;EACZ;EAKQ,OAAO,EAAQ;EACf,MAAM,EAAQ;EACd,iBAAiB,EAAQ;EACzB,YAAY,EAAQ;EACpB,iBAAiB,EAAQ;YAExB,EAAQ;CACN,CAAA;AAEf;AAOA,SAAS,EAAa,EAAE,UAAO,YAA4B;CACvD,IAAM,EAAE,YAAS,UAAO,GAClB,CAAC,GAAS,KAAc,EAAS,EAAK;CAiB5C,OACI,kBAAC,GAAD;EACI,MAAA;EACA,OAAO,EAAQ,SAAS;EACxB,aAAa,EAAQ;EACrB,cAAc,EAAQ;EACtB,aAAa,EAAQ;EACrB,SAAS,EAAQ,SAAS,WAAW;EAC5B;EACT,WAAW,YAxB8B;GAC7C,IAAI;IAEA,AADA,EAAW,EAAI,GACf,MAAM,EAAQ,YAAY;GAC9B,UAAU;IAEN,AADA,EAAW,EAAK,GAChB,EAAM,CAAE;GACZ;EACJ;EAiBQ,gBAfyB;GAE7B,AADA,EAAQ,WAAW,GACnB,EAAM,CAAE;EACZ;CAaK,CAAA;AAET;AAWA,SAAgB,IAAuB;CACnC,IAAM,IAAM,EAAW,CAAa;CACpC,IAAI,MAAQ,MACR,MAAU,MAAM,kDAAkD;CAEtE,OAAO;AACX"}
1
+ {"version":3,"file":"ModalsManager.js","names":[],"sources":["../../../src/components/ModalsManager/ModalsManager.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the imperative API (open, close, confirm, prompt) and\n * the stack that renders it live in one file so the returned promise and the mounted\n * element cannot drift apart.\n */\nimport { createContext, useCallback, useContext, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { ConfirmDialog } from \"@/components/ConfirmDialog\";\nimport { Modal } from \"@/components/Modal\";\nimport type { ModalSize } from \"@/components/Modal\";\nimport styles from \"./ModalsManager.module.css\";\n\n/** Options accepted by {@link ModalsApi.open}. */\nexport interface OpenModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Modal body content. */\n children: ReactNode;\n /** Dialog size. Default `md`. */\n size?: ModalSize;\n /** Allow closing by clicking the backdrop. Default `true`. */\n closeOnBackdrop?: boolean;\n /** Allow closing with the Esc key. Default `true`. */\n closeOnEsc?: boolean;\n /** Hide the header close button. */\n hideCloseButton?: boolean;\n /** Called after the modal is removed from the stack. */\n onClose?: () => void;\n}\n\n/** Options accepted by {@link ModalsApi.confirm}. */\nexport interface ConfirmModalOptions {\n /** Header title. */\n title?: ReactNode;\n /** Prompt message rendered in the dialog body. */\n message: ReactNode;\n /** Confirm button label. Default `Confirmar`. */\n confirmLabel?: string;\n /** Cancel button label. Default `Cancelar`. */\n cancelLabel?: string;\n /** Render the confirm button in the danger variant. */\n danger?: boolean;\n /** Called when the user confirms. */\n onConfirm?: () => void | Promise<void>;\n /** Called when the user cancels or dismisses. */\n onCancel?: () => void;\n}\n\n/** Imperative API returned by {@link useModals}. */\nexport interface ModalsApi {\n /** Push a content modal. Returns its stack id. */\n open: (options: OpenModalOptions) => string;\n /** Push a confirmation dialog. Returns its stack id. */\n confirm: (options: ConfirmModalOptions) => string;\n /** Remove the modal with the given id. */\n close: (id: string) => void;\n /** Remove every modal from the stack. */\n closeAll: () => void;\n}\n\ninterface ContentEntry {\n kind: \"content\";\n id: string;\n options: OpenModalOptions;\n}\n\ninterface ConfirmEntry {\n kind: \"confirm\";\n id: string;\n options: ConfirmModalOptions;\n}\n\ntype StackEntry = ContentEntry | ConfirmEntry;\n\nconst ModalsContext = createContext<ModalsApi | null>(null);\n\n/** Props for {@link ModalsProvider}. */\nexport interface ModalsProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides imperative modal control via {@link useModals} and renders the open\n * modal stack. Mount once near the app root.\n *\n * @example\n * <ModalsProvider>\n * <App />\n * </ModalsProvider>\n */\nexport function ModalsProvider({ children }: ModalsProviderProps) {\n const [stack, setStack] = useState<StackEntry[]>([]);\n const counter = useRef(0);\n\n const nextId = useCallback((): string => {\n counter.current += 1;\n return `modal-${counter.current}`;\n }, []);\n\n const close = useCallback((id: string): void => {\n setStack((current) => current.filter((entry) => entry.id !== id));\n }, []);\n\n const closeAll = useCallback((): void => {\n setStack([]);\n }, []);\n\n const open = useCallback(\n (options: OpenModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"content\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const confirm = useCallback(\n (options: ConfirmModalOptions): string => {\n const id = nextId();\n setStack((current) => [...current, { kind: \"confirm\", id, options }]);\n return id;\n },\n [nextId],\n );\n\n const api = useMemo<ModalsApi>(\n () => ({ open, confirm, close, closeAll }),\n [open, confirm, close, closeAll],\n );\n\n return (\n <ModalsContext.Provider value={api}>\n {children}\n <div className={styles.stack}>\n {stack.map((entry) =>\n entry.kind === \"content\" ? (\n <ContentModal key={entry.id} entry={entry} close={close} />\n ) : (\n <ConfirmModal key={entry.id} entry={entry} close={close} />\n ),\n )}\n </div>\n </ModalsContext.Provider>\n );\n}\n\ninterface ContentModalProps {\n entry: ContentEntry;\n close: (id: string) => void;\n}\n\nfunction ContentModal({ entry, close }: ContentModalProps) {\n const { options, id } = entry;\n const handleClose = (): void => {\n options.onClose?.();\n close(id);\n };\n return (\n <Modal\n open\n onClose={handleClose}\n title={options.title}\n size={options.size}\n closeOnBackdrop={options.closeOnBackdrop}\n closeOnEsc={options.closeOnEsc}\n hideCloseButton={options.hideCloseButton}\n >\n {options.children}\n </Modal>\n );\n}\n\ninterface ConfirmModalProps {\n entry: ConfirmEntry;\n close: (id: string) => void;\n}\n\nfunction ConfirmModal({ entry, close }: ConfirmModalProps) {\n const { options, id } = entry;\n const [loading, setLoading] = useState(false);\n\n const handleConfirm = async (): Promise<void> => {\n try {\n setLoading(true);\n await options.onConfirm?.();\n } finally {\n setLoading(false);\n close(id);\n }\n };\n\n const handleCancel = (): void => {\n options.onCancel?.();\n close(id);\n };\n\n return (\n <ConfirmDialog\n open\n title={options.title ?? \"\"}\n description={options.message}\n confirmLabel={options.confirmLabel}\n cancelLabel={options.cancelLabel}\n variant={options.danger ? \"danger\" : \"primary\"}\n loading={loading}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n );\n}\n\n/**\n * Access the imperative modals API. Must be used within a {@link ModalsProvider}.\n *\n * @example\n * const modals = useModals();\n * modals.confirm({ message: \"Excluir item?\", danger: true, onConfirm: del });\n *\n * @throws Error when called outside a {@link ModalsProvider}.\n */\nexport function useModals(): ModalsApi {\n const api = useContext(ModalsContext);\n if (api === null) {\n throw new Error(\"useModals must be used within a <ModalsProvider>\");\n }\n return api;\n}\n"],"mappings":";;;;;;AA0EA,IAAM,IAAgB,EAAgC,IAAI;AAgB1D,SAAgB,EAAe,EAAE,eAAiC;CAC9D,IAAM,CAAC,GAAO,KAAY,EAAuB,CAAC,CAAC,GAC7C,IAAU,EAAO,CAAC,GAElB,IAAS,SACX,EAAQ,WAAW,GACZ,SAAS,EAAQ,YACzB,CAAC,CAAC,GAEC,IAAQ,GAAa,MAAqB;EAC5C,GAAU,MAAY,EAAQ,QAAQ,MAAU,EAAM,OAAO,CAAE,CAAC;CACpE,GAAG,CAAC,CAAC,GAEC,IAAW,QAAwB;EACrC,EAAS,CAAC,CAAC;CACf,GAAG,CAAC,CAAC,GAEC,IAAO,GACR,MAAsC;EACnC,IAAM,IAAK,EAAO;EAElB,OADA,GAAU,MAAY,CAAC,GAAG,GAAS;GAAE,MAAM;GAAW;GAAI;EAAQ,CAAC,CAAC,GAC7D;CACX,GACA,CAAC,CAAM,CACX,GAEM,IAAU,GACX,MAAyC;EACtC,IAAM,IAAK,EAAO;EAElB,OADA,GAAU,MAAY,CAAC,GAAG,GAAS;GAAE,MAAM;GAAW;GAAI;EAAQ,CAAC,CAAC,GAC7D;CACX,GACA,CAAC,CAAM,CACX,GAEM,IAAM,SACD;EAAE;EAAM;EAAS;EAAO;CAAS,IACxC;EAAC;EAAM;EAAS;EAAO;CAAQ,CACnC;CAEA,OACI,kBAAC,EAAc,UAAf;EAAwB,OAAO;EAA/B,UAAA,CACK,GACD,kBAAC,OAAD;GAAK,WAAW,EAAO;GAClB,UAAA,EAAM,KAAK,MACR,EAAM,SAAS,YACX,kBAAC,GAAD;IAAoC;IAAc;GAAQ,GAAvC,EAAM,EAAiC,IAE1D,kBAAC,GAAD;IAAoC;IAAc;GAAQ,GAAvC,EAAM,EAAiC,CAElE;EACC,CAAA,CACe;;AAEhC;AAOA,SAAS,EAAa,EAAE,UAAO,YAA4B;CACvD,IAAM,EAAE,YAAS,UAAO;CAKxB,OACI,kBAAC,GAAD;EACI,MAAA;EACA,eAPwB;GAE5B,AADA,EAAQ,UAAU,GAClB,EAAM,CAAE;EACZ;EAKQ,OAAO,EAAQ;EACf,MAAM,EAAQ;EACd,iBAAiB,EAAQ;EACzB,YAAY,EAAQ;EACpB,iBAAiB,EAAQ;EAExB,UAAA,EAAQ;CACN,CAAA;AAEf;AAOA,SAAS,EAAa,EAAE,UAAO,YAA4B;CACvD,IAAM,EAAE,YAAS,UAAO,GAClB,CAAC,GAAS,KAAc,EAAS,EAAK;CAiB5C,OACI,kBAAC,GAAD;EACI,MAAA;EACA,OAAO,EAAQ,SAAS;EACxB,aAAa,EAAQ;EACrB,cAAc,EAAQ;EACtB,aAAa,EAAQ;EACrB,SAAS,EAAQ,SAAS,WAAW;EAC5B;EACT,WAAW,YAxB8B;GAC7C,IAAI;IAEA,AADA,EAAW,EAAI,GACf,MAAM,EAAQ,YAAY;GAC9B,UAAU;IAEN,AADA,EAAW,EAAK,GAChB,EAAM,CAAE;GACZ;EACJ;EAiBQ,gBAfyB;GAE7B,AADA,EAAQ,WAAW,GACnB,EAAM,CAAE;EACZ;CAaK,CAAA;AAET;AAWA,SAAgB,IAAuB;CACnC,IAAM,IAAM,EAAW,CAAa;CACpC,IAAI,MAAQ,MACR,MAAU,MAAM,kDAAkD;CAEtE,OAAO;AACX"}
@@ -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,WAAQ,CAAgB,CAAA,CAC7C"}
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 +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;YAAQ;CAAgB,CAAA;AAC7C"}
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"}