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":"MultiSelect.cjs","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, and the three knobs that make it multi: maxItems,\n * filter, emptyMessage. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate, Enter toggles the active option, Esc\n * closes, Backspace (empty input) pops the last chip.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n setOpen(false);\n setQuery(\"\");\n }\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n setOpen(false);\n setQuery(\"\");\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={(event) => {\n if (disabled) return;\n // Keep focus on the input when clicking empty field space.\n if (event.target === event.currentTarget) {\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n }\n }}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n </div>\n {open && (\n <ul\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={styles.menu}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n toggle(option);\n }}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n )}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelect.cjs","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, and the three knobs that make it multi: maxItems,\n * filter, emptyMessage. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate, Enter toggles the active option, Esc\n * closes, Backspace (empty input) pops the last chip.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n setOpen(false);\n setQuery(\"\");\n }\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n setOpen(false);\n setQuery(\"\");\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={(event) => {\n if (disabled) return;\n // Keep focus on the input when clicking empty field space.\n if (event.target === event.currentTarget) {\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n }\n }}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n </div>\n {open && (\n <ul\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={styles.menu}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n toggle(option);\n }}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n )}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MultiSelect.js","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, and the three knobs that make it multi: maxItems,\n * filter, emptyMessage. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate, Enter toggles the active option, Esc\n * closes, Backspace (empty input) pops the last chip.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n setOpen(false);\n setQuery(\"\");\n }\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n setOpen(false);\n setQuery(\"\");\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={(event) => {\n if (disabled) return;\n // Keep focus on the input when clicking empty field space.\n if (event.target === event.currentTarget) {\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n }\n }}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n </div>\n {open && (\n <ul\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={styles.menu}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n toggle(option);\n }}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n )}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MultiSelect.js","names":[],"sources":["../../../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — options/value/onChange\n * plus the shared field contract, and the three knobs that make it multi: maxItems,\n * filter, emptyMessage. The body is the listbox with chip removal, type-ahead and\n * the ARIA active-descendant wiring reading one active index.\n */\nimport { useCallback, useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./MultiSelect.module.css\";\n\nexport interface MultiSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface MultiSelectProps {\n options: MultiSelectOption[];\n /** Currently selected values. */\n value: string[];\n onChange: (value: string[]) => void;\n label?: string;\n placeholder?: string;\n helperText?: string;\n error?: string;\n disabled?: boolean;\n /** Cap the number of selectable items. */\n maxItems?: number;\n /** Custom filter — return true to keep the option. Default: case-insensitive substring on label. */\n filter?: (option: MultiSelectOption, query: string) => boolean;\n /** Message shown when no option matches. */\n emptyMessage?: string;\n className?: string;\n}\n\nfunction defaultFilter(option: MultiSelectOption, query: string): boolean {\n if (!query) return true;\n return option.label.toLowerCase().includes(query.toLowerCase());\n}\n\n/**\n * MultiSelect — a filterable dropdown that selects many options, shown as\n * removable chips inside the field. Selecting toggles a value; Backspace on an\n * empty query removes the last chip.\n *\n * Keyboard: ArrowUp/ArrowDown navigate, Enter toggles the active option, Esc\n * closes, Backspace (empty input) pops the last chip.\n */\nexport function MultiSelect({\n options,\n value,\n onChange,\n label,\n placeholder = \"Selecione\",\n helperText,\n error,\n disabled,\n maxItems,\n filter = defaultFilter,\n emptyMessage = \"Nenhuma opção encontrada\",\n className,\n}: MultiSelectProps) {\n const id = useId();\n const rootRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [activeIndex, setActiveIndex] = useState(0);\n\n const selectedSet = useMemo(() => new Set(value), [value]);\n const selectedOptions = useMemo(\n () =>\n value\n .map((v) => options.find((o) => o.value === v))\n .filter((o): o is MultiSelectOption => Boolean(o)),\n [value, options],\n );\n const filtered = useMemo(\n () => options.filter((o) => filter(o, query)),\n [options, filter, query],\n );\n\n const atMax = maxItems !== undefined && value.length >= maxItems;\n\n const toggle = useCallback(\n (option: MultiSelectOption): void => {\n if (option.disabled) return;\n if (selectedSet.has(option.value)) {\n onChange(value.filter((v) => v !== option.value));\n } else {\n if (atMax) return;\n onChange([...value, option.value]);\n }\n setQuery(\"\");\n inputRef.current?.focus();\n },\n [selectedSet, onChange, value, atMax],\n );\n\n const removeAt = useCallback(\n (optionValue: string): void => {\n onChange(value.filter((v) => v !== optionValue));\n },\n [onChange, value],\n );\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) {\n setOpen(false);\n setQuery(\"\");\n }\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setOpen(true);\n setActiveIndex((current) => Math.min(filtered.length - 1, current + 1));\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((current) => Math.max(0, current - 1));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n const option = filtered[activeIndex];\n if (option) toggle(option);\n } else if (event.key === \"Escape\") {\n setOpen(false);\n setQuery(\"\");\n } else if (event.key === \"Backspace\" && query === \"\" && value.length > 0) {\n removeAt(value[value.length - 1]);\n }\n };\n\n return (\n <div ref={rootRef} className={cn(styles.wrapper, error && styles.error, className)}>\n {label && (\n <label htmlFor={id} className={styles.label}>\n {label}\n </label>\n )}\n <div\n className={cn(styles.field, disabled && styles.disabled)}\n onMouseDown={(event) => {\n if (disabled) return;\n // Keep focus on the input when clicking empty field space.\n if (event.target === event.currentTarget) {\n event.preventDefault();\n inputRef.current?.focus();\n setOpen(true);\n }\n }}\n >\n {selectedOptions.map((option) => (\n <span key={option.value} className={styles.chip}>\n {option.label}\n <button\n type=\"button\"\n className={styles.chipRemove}\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n onMouseDown={(event) => {\n event.preventDefault();\n removeAt(option.value);\n }}\n >\n ×\n </button>\n </span>\n ))}\n <input\n id={id}\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={open}\n aria-controls={`${id}-listbox`}\n aria-autocomplete=\"list\"\n className={styles.input}\n placeholder={value.length === 0 ? placeholder : \"\"}\n disabled={disabled}\n value={query}\n onFocus={() => setOpen(true)}\n onChange={(event) => {\n setQuery(event.target.value);\n setOpen(true);\n setActiveIndex(0);\n }}\n onKeyDown={handleKeyDown}\n />\n </div>\n {open && (\n <ul\n id={`${id}-listbox`}\n role=\"listbox\"\n aria-multiselectable\n className={styles.menu}\n >\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n filtered.map((option, index) => {\n const isSelected = selectedSet.has(option.value);\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || (!isSelected && atMax)}\n className={cn(\n styles.option,\n index === activeIndex && styles.active,\n isSelected && styles.selected,\n )}\n onMouseEnter={() => setActiveIndex(index)}\n onMouseDown={(event) => {\n event.preventDefault();\n toggle(option);\n }}\n >\n <span className={styles.check} aria-hidden>\n {isSelected ? \"✓\" : \"\"}\n </span>\n {option.label}\n </li>\n );\n })\n )}\n </ul>\n )}\n {error ? (\n <span className={styles.errorText}>{error}</span>\n ) : helperText ? (\n <span className={styles.helper}>{helperText}</span>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"NProgress.cjs","names":[],"sources":["../../../src/components/NProgress/NProgress.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NProgress.module.css\";\n\n/** Snapshot of the progress controller state delivered to subscribers. */\nexport interface NProgressState {\n /** Current progress, `0` (empty) to `1` (complete). */\n value: number;\n /** Whether the bar should be visible. */\n active: boolean;\n}\n\n/** Listener invoked whenever the progress state changes. */\nexport type NProgressListener = (state: NProgressState) => void;\n\n/** Imperative top-loading-bar controller. */\nexport interface NProgressController {\n /** Show the bar and start trickling toward ~0.9. */\n start: () => void;\n /** Complete to `1`, then hide the bar shortly after. */\n done: () => void;\n /** Set the progress explicitly. Values are clamped to `0..1`. */\n set: (n: number) => void;\n /** Increment progress by `amount` (default a small trickle step). */\n inc: (amount?: number) => void;\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe: (listener: NProgressListener) => () => void;\n}\n\nconst TRICKLE_INTERVAL_MS = 300;\nconst TRICKLE_CEILING = 0.9;\nconst DONE_HIDE_DELAY_MS = 300;\n\nfunction clamp(n: number): number {\n if (n < 0) return 0;\n if (n > 1) return 1;\n return n;\n}\n\nfunction createNProgress(): NProgressController {\n let state: NProgressState = { value: 0, active: false };\n const listeners = new Set<NProgressListener>();\n let trickleTimer: ReturnType<typeof setInterval> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n const emit = (): void => {\n for (const listener of listeners) listener(state);\n };\n\n const update = (next: Partial<NProgressState>): void => {\n state = { ...state, ...next };\n emit();\n };\n\n const stopTrickle = (): void => {\n if (trickleTimer !== null) {\n clearInterval(trickleTimer);\n trickleTimer = null;\n }\n };\n\n const clearHide = (): void => {\n if (hideTimer !== null) {\n clearTimeout(hideTimer);\n hideTimer = null;\n }\n };\n\n const inc = (amount?: number): void => {\n const current = state.value;\n if (current >= TRICKLE_CEILING) return;\n const step = amount ?? (1 - current) * 0.1 + 0.01;\n update({ value: Math.min(clamp(current + step), TRICKLE_CEILING) });\n };\n\n const start = (): void => {\n clearHide();\n if (state.active) return;\n update({ active: true, value: state.value > 0 ? state.value : 0.08 });\n stopTrickle();\n trickleTimer = setInterval(() => inc(), TRICKLE_INTERVAL_MS);\n };\n\n const done = (): void => {\n stopTrickle();\n clearHide();\n update({ value: 1, active: true });\n hideTimer = setTimeout(() => {\n update({ active: false, value: 0 });\n hideTimer = null;\n }, DONE_HIDE_DELAY_MS);\n };\n\n const set = (n: number): void => {\n clearHide();\n const value = clamp(n);\n update({ value, active: value < 1 ? true : state.active });\n };\n\n const subscribe = (listener: NProgressListener): (() => void) => {\n listeners.add(listener);\n listener(state);\n return () => {\n listeners.delete(listener);\n };\n };\n\n return { start, done, set, inc, subscribe };\n}\n\n/**\n * Module-level singleton progress controller. Drive it imperatively from\n * anywhere (router transitions, fetch interceptors) and render the visual bar\n * with {@link NProgressBar}.\n *\n * @example\n * nprogress.start();\n * await loadData();\n * nprogress.done();\n */\nexport const nprogress: NProgressController = createNProgress();\n\n/** Props for {@link NProgressBar}. */\nexport interface NProgressBarProps {\n /** Bar color. Defaults to `var(--tempest-primary)`. */\n color?: string;\n /** Bar height in pixels. Default `3`. */\n height?: number;\n /** Extra class applied to the bar element. */\n className?: string;\n}\n\n/**\n * Fixed top loading bar bound to the {@link nprogress} singleton. Mount once\n * near the app root; it renders nothing while inactive.\n *\n * @example\n * <NProgressBar />\n */\nexport function NProgressBar({ color, height = 3, className }: NProgressBarProps) {\n const [state, setState] = useState<NProgressState>({ value: 0, active: false });\n\n useEffect(() => nprogress.subscribe(setState), []);\n\n if (!state.active) return null;\n\n const style: CSSProperties = {\n width: `${Math.round(state.value * 100)}%`,\n height: `${height}px`,\n background: color ?? \"var(--tempest-primary)\",\n };\n\n return (\n <div\n className={cn(styles.bar, className)}\n style={style}\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(state.value * 100)}\n />\n );\n}\n"],"mappings":"gIA8BA,IAAM,EAAsB,IACtB,EAAkB,GAClB,EAAqB,IAE3B,SAAS,EAAM,EAAmB,CAG9B,OAFI,EAAI,EAAU,EACd,EAAI,EAAU,EACX,CACX,CAEA,SAAS,GAAuC,CAC5C,IAAI,EAAwB,CAAE,MAAO,EAAG,OAAQ,EAAM,EAChD,EAAY,IAAI,IAClB,EAAsD,KACtD,EAAkD,KAEhD,MAAmB,CACrB,IAAK,IAAM,KAAY,EAAW,EAAS,CAAK,CACpD,EAEM,EAAU,GAAwC,CACpD,EAAQ,CAAE,GAAG,EAAO,GAAG,CAAK,EAC5B,EAAK,CACT,EAEM,MAA0B,CACxB,IAAiB,OACjB,cAAc,CAAY,EAC1B,EAAe,KAEvB,EAEM,MAAwB,CACtB,IAAc,OACd,aAAa,CAAS,EACtB,EAAY,KAEpB,EAEM,EAAO,GAA0B,CACnC,IAAM,EAAU,EAAM,MACtB,GAAI,GAAW,EAAiB,OAChC,IAAM,EAAO,IAAW,EAAI,GAAW,GAAM,IAC7C,EAAO,CAAE,MAAO,KAAK,IAAI,EAAM,EAAU,CAAI,EAAG,CAAe,CAAE,CAAC,CACtE,EAkCA,MAAO,CAAE,UAhCiB,CACtB,EAAU,EACN,GAAM,SACV,EAAO,CAAE,OAAQ,GAAM,MAAO,EAAM,MAAQ,EAAI,EAAM,MAAQ,GAAK,CAAC,EACpE,EAAY,EACZ,EAAe,gBAAkB,EAAI,EAAG,CAAmB,EAC/D,EA0BgB,SAxBS,CACrB,EAAY,EACZ,EAAU,EACV,EAAO,CAAE,MAAO,EAAG,OAAQ,EAAK,CAAC,EACjC,EAAY,eAAiB,CACzB,EAAO,CAAE,OAAQ,GAAO,MAAO,CAAE,CAAC,EAClC,EAAY,IAChB,EAAG,CAAkB,CACzB,EAgBsB,IAdT,GAAoB,CAC7B,EAAU,EACV,IAAM,EAAQ,EAAM,CAAC,EACrB,EAAO,CAAE,QAAO,OAAQ,EAAQ,GAAW,EAAM,MAAO,CAAC,CAC7D,EAU2B,MAAK,UARb,IACf,EAAU,IAAI,CAAQ,EACtB,EAAS,CAAK,MACD,CACT,EAAU,OAAO,CAAQ,CAC7B,EAGsC,CAC9C,CAYA,IAAa,EAAiC,EAAgB,EAmB9D,SAAgB,EAAa,CAAE,QAAO,SAAS,EAAG,aAAgC,CAC9E,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,CAAqC,CAAE,MAAO,EAAG,OAAQ,EAAM,CAAC,EAI9E,IAFA,EAAA,EAAA,UAAA,KAAgB,EAAU,UAAU,CAAQ,EAAG,CAAC,CAAC,EAE7C,CAAC,EAAM,OAAQ,OAAO,KAE1B,IAAM,EAAuB,CACzB,MAAO,GAAG,KAAK,MAAM,EAAM,MAAQ,GAAG,EAAE,GACxC,OAAQ,GAAG,EAAO,IAClB,WAAY,GAAS,wBACzB,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,CAAS,EAC5B,QACP,KAAK,cACL,gBAAe,EACf,gBAAe,IACf,gBAAe,KAAK,MAAM,EAAM,MAAQ,GAAG,CAC9C,CAAA,CAET"}
1
+ {"version":3,"file":"NProgress.cjs","names":[],"sources":["../../../src/components/NProgress/NProgress.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NProgress.module.css\";\n\n/** Snapshot of the progress controller state delivered to subscribers. */\nexport interface NProgressState {\n /** Current progress, `0` (empty) to `1` (complete). */\n value: number;\n /** Whether the bar should be visible. */\n active: boolean;\n}\n\n/** Listener invoked whenever the progress state changes. */\nexport type NProgressListener = (state: NProgressState) => void;\n\n/** Imperative top-loading-bar controller. */\nexport interface NProgressController {\n /** Show the bar and start trickling toward ~0.9. */\n start: () => void;\n /** Complete to `1`, then hide the bar shortly after. */\n done: () => void;\n /** Set the progress explicitly. Values are clamped to `0..1`. */\n set: (n: number) => void;\n /** Increment progress by `amount` (default a small trickle step). */\n inc: (amount?: number) => void;\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe: (listener: NProgressListener) => () => void;\n}\n\nconst TRICKLE_INTERVAL_MS = 300;\nconst TRICKLE_CEILING = 0.9;\nconst DONE_HIDE_DELAY_MS = 300;\n\nfunction clamp(n: number): number {\n if (n < 0) return 0;\n if (n > 1) return 1;\n return n;\n}\n\nfunction createNProgress(): NProgressController {\n let state: NProgressState = { value: 0, active: false };\n const listeners = new Set<NProgressListener>();\n let trickleTimer: ReturnType<typeof setInterval> | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n\n const emit = (): void => {\n for (const listener of listeners) listener(state);\n };\n\n const update = (next: Partial<NProgressState>): void => {\n state = { ...state, ...next };\n emit();\n };\n\n const stopTrickle = (): void => {\n if (trickleTimer !== null) {\n clearInterval(trickleTimer);\n trickleTimer = null;\n }\n };\n\n const clearHide = (): void => {\n if (hideTimer !== null) {\n clearTimeout(hideTimer);\n hideTimer = null;\n }\n };\n\n const inc = (amount?: number): void => {\n const current = state.value;\n if (current >= TRICKLE_CEILING) return;\n const step = amount ?? (1 - current) * 0.1 + 0.01;\n update({ value: Math.min(clamp(current + step), TRICKLE_CEILING) });\n };\n\n const start = (): void => {\n clearHide();\n if (state.active) return;\n update({ active: true, value: state.value > 0 ? state.value : 0.08 });\n stopTrickle();\n trickleTimer = setInterval(() => inc(), TRICKLE_INTERVAL_MS);\n };\n\n const done = (): void => {\n stopTrickle();\n clearHide();\n update({ value: 1, active: true });\n hideTimer = setTimeout(() => {\n update({ active: false, value: 0 });\n hideTimer = null;\n }, DONE_HIDE_DELAY_MS);\n };\n\n const set = (n: number): void => {\n clearHide();\n const value = clamp(n);\n update({ value, active: value < 1 ? true : state.active });\n };\n\n const subscribe = (listener: NProgressListener): (() => void) => {\n listeners.add(listener);\n listener(state);\n return () => {\n listeners.delete(listener);\n };\n };\n\n return { start, done, set, inc, subscribe };\n}\n\n/**\n * Module-level singleton progress controller. Drive it imperatively from\n * anywhere (router transitions, fetch interceptors) and render the visual bar\n * with {@link NProgressBar}.\n *\n * @example\n * nprogress.start();\n * await loadData();\n * nprogress.done();\n */\nexport const nprogress: NProgressController = createNProgress();\n\n/** Props for {@link NProgressBar}. */\nexport interface NProgressBarProps {\n /** Bar color. Defaults to `var(--tempest-primary)`. */\n color?: string;\n /** Bar height in pixels. Default `3`. */\n height?: number;\n /** Extra class applied to the bar element. */\n className?: string;\n}\n\n/**\n * Fixed top loading bar bound to the {@link nprogress} singleton. Mount once\n * near the app root; it renders nothing while inactive.\n *\n * @example\n * <NProgressBar />\n */\nexport function NProgressBar({ color, height = 3, className }: NProgressBarProps) {\n const [state, setState] = useState<NProgressState>({ value: 0, active: false });\n\n useEffect(() => nprogress.subscribe(setState), []);\n\n if (!state.active) return null;\n\n const style: CSSProperties = {\n width: `${Math.round(state.value * 100)}%`,\n height: `${height}px`,\n background: color ?? \"var(--tempest-primary)\",\n };\n\n return (\n <div\n className={cn(styles.bar, className)}\n style={style}\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(state.value * 100)}\n />\n );\n}\n"],"mappings":"gIA8BA,IAAM,EAAsB,IACtB,EAAkB,GAClB,EAAqB,IAE3B,SAAS,EAAM,EAAmB,CAG9B,OAFI,EAAI,EAAU,EACd,EAAI,EAAU,EACX,CACX,CAEA,SAAS,GAAuC,CAC5C,IAAI,EAAwB,CAAE,MAAO,EAAG,OAAQ,EAAM,EAChD,EAAY,IAAI,IAClB,EAAsD,KACtD,EAAkD,KAEhD,MAAmB,CACrB,IAAK,IAAM,KAAY,EAAW,EAAS,CAAK,CACpD,EAEM,EAAU,GAAwC,CACpD,EAAQ,CAAE,GAAG,EAAO,GAAG,CAAK,EAC5B,EAAK,CACT,EAEM,MAA0B,CACxB,IAAiB,OACjB,cAAc,CAAY,EAC1B,EAAe,KAEvB,EAEM,MAAwB,CACtB,IAAc,OACd,aAAa,CAAS,EACtB,EAAY,KAEpB,EAEM,EAAO,GAA0B,CACnC,IAAM,EAAU,EAAM,MACtB,GAAI,GAAW,EAAiB,OAChC,IAAM,EAAO,IAAW,EAAI,GAAW,GAAM,IAC7C,EAAO,CAAE,MAAO,KAAK,IAAI,EAAM,EAAU,CAAI,EAAG,CAAe,CAAE,CAAC,CACtE,EAkCA,MAAO,CAAE,UAhCiB,CACtB,EAAU,EACN,GAAM,SACV,EAAO,CAAE,OAAQ,GAAM,MAAO,EAAM,MAAQ,EAAI,EAAM,MAAQ,GAAK,CAAC,EACpE,EAAY,EACZ,EAAe,gBAAkB,EAAI,EAAG,CAAmB,EAC/D,EA0BgB,SAxBS,CACrB,EAAY,EACZ,EAAU,EACV,EAAO,CAAE,MAAO,EAAG,OAAQ,EAAK,CAAC,EACjC,EAAY,eAAiB,CACzB,EAAO,CAAE,OAAQ,GAAO,MAAO,CAAE,CAAC,EAClC,EAAY,IAChB,EAAG,CAAkB,CACzB,EAgBsB,IAdT,GAAoB,CAC7B,EAAU,EACV,IAAM,EAAQ,EAAM,CAAC,EACrB,EAAO,CAAE,QAAO,OAAQ,EAAQ,GAAW,EAAM,MAAO,CAAC,CAC7D,EAU2B,MAAK,UARb,IACf,EAAU,IAAI,CAAQ,EACtB,EAAS,CAAK,MACD,CACT,EAAU,OAAO,CAAQ,CAC7B,EAGsC,CAC9C,CAYA,IAAa,EAAiC,EAAgB,EAmB9D,SAAgB,EAAa,CAAE,QAAO,SAAS,EAAG,aAAgC,CAC9E,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAyB,CAAE,MAAO,EAAG,OAAQ,EAAM,CAAC,EAI9E,IAFA,EAAA,EAAA,UAAA,KAAgB,EAAU,UAAU,CAAQ,EAAG,CAAC,CAAC,EAE7C,CAAC,EAAM,OAAQ,OAAO,KAE1B,IAAM,EAAuB,CACzB,MAAO,GAAG,KAAK,MAAM,EAAM,MAAQ,GAAG,EAAE,GACxC,OAAQ,GAAG,EAAO,IAClB,WAAY,GAAS,wBACzB,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,CAAS,EAC5B,QACP,KAAK,cACL,gBAAe,EACf,gBAAe,IACf,gBAAe,KAAK,MAAM,EAAM,MAAQ,GAAG,CAC9C,CAAA,CAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"Navbar.cjs","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\nexport type NavbarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /** Visual tone. Default `\"surface\"`. */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":"0GAgCA,SAAgB,EAAO,CACnB,OACA,MACA,UACA,SAAS,GACT,OAAO,UACP,WAAW,GACX,YACA,GAAG,GACS,CACZ,OACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAA,QAAO,GACP,GAAU,EAAA,QAAO,OACjB,GAAY,EAAA,QAAO,SACnB,CACJ,EACA,GAAI,WARR,CAUK,IAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,cAAO,CAAU,CAAA,EAChD,IAAO,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,aAAM,CAAS,CAAA,EAC7C,IAAW,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,iBAAU,CAAa,CAAA,CACtD,GAEhB"}
1
+ {"version":3,"file":"Navbar.cjs","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\nexport type NavbarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /** Visual tone. Default `\"surface\"`. */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":"0GAgCA,SAAgB,EAAO,CACnB,OACA,MACA,UACA,SAAS,GACT,OAAO,UACP,WAAW,GACX,YACA,GAAG,GACS,CACZ,OACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAA,QAAO,GACP,GAAU,EAAA,QAAO,OACjB,GAAY,EAAA,QAAO,SACnB,CACJ,EACA,GAAI,EARR,SAAA,CAUK,IAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,SAAA,CAAU,CAAA,EAChD,IAAO,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,IAAM,SAAA,CAAS,CAAA,EAC7C,IAAW,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAAU,SAAA,CAAa,CAAA,CACtD,GAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Navbar.js","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\nexport type NavbarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /** Visual tone. Default `\"surface\"`. */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":";;;;AAgCA,SAAgB,EAAO,EACnB,SACA,QACA,YACA,YAAS,IACT,UAAO,WACP,cAAW,IACX,cACA,GAAG,KACS;CACZ,OACI,kBAAC,UAAD;EACI,WAAW,EACP,EAAO,QACP,EAAO,IACP,KAAU,EAAO,QACjB,KAAY,EAAO,UACnB,CACJ;EACA,GAAI;YARR;GAUK,KAAQ,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAO;GAAU,CAAA;GAChD,KAAO,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAM;GAAS,CAAA;GAC7C,KAAW,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAU;GAAa,CAAA;EACtD;;AAEhB"}
1
+ {"version":3,"file":"Navbar.js","names":[],"sources":["../../../src/components/Navbar/Navbar.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Navbar.module.css\";\n\nexport type NavbarTone = \"surface\" | \"primary\" | \"transparent\";\n\nexport interface NavbarProps extends HTMLAttributes<HTMLElement> {\n /** Left slot — typically logo + brand name. */\n logo?: ReactNode;\n /** Center slot — typically nav links. */\n nav?: ReactNode;\n /** Right slot — typically user menu / actions. */\n actions?: ReactNode;\n /** Make the bar sticky at the top of the scroll container. Default `true`. */\n sticky?: boolean;\n /** Visual tone. Default `\"surface\"`. */\n tone?: NavbarTone;\n /** When set, renders a thin bottom border. Default `true`. */\n bordered?: boolean;\n}\n\n/**\n * Top app bar. Three-slot layout (logo / nav / actions) that collapses\n * gracefully on mobile (nav slot wraps below).\n *\n * @example\n * <Navbar\n * logo={<img src=\"/logo.svg\" alt=\"App\" />}\n * nav={<NavLinks />}\n * actions={<UserMenu />}\n * />\n */\nexport function Navbar({\n logo,\n nav,\n actions,\n sticky = true,\n tone = \"surface\",\n bordered = true,\n className,\n ...props\n}: NavbarProps) {\n return (\n <header\n className={cn(\n styles.navbar,\n styles[tone],\n sticky && styles.sticky,\n bordered && styles.bordered,\n className,\n )}\n {...props}\n >\n {logo && <div className={styles.logo}>{logo}</div>}\n {nav && <nav className={styles.nav}>{nav}</nav>}\n {actions && <div className={styles.actions}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":";;;;AAgCA,SAAgB,EAAO,EACnB,SACA,QACA,YACA,YAAS,IACT,UAAO,WACP,cAAW,IACX,cACA,GAAG,KACS;CACZ,OACI,kBAAC,UAAD;EACI,WAAW,EACP,EAAO,QACP,EAAO,IACP,KAAU,EAAO,QACjB,KAAY,EAAO,UACnB,CACJ;EACA,GAAI;EARR,UAAA;GAUK,KAAQ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAO,UAAA;GAAU,CAAA;GAChD,KAAO,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAM,UAAA;GAAS,CAAA;GAC7C,KAAW,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAU,UAAA;GAAa,CAAA;EACtD;;AAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationMenu.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n /** Visible label for the entry. */\n label: ReactNode;\n /** When set, the entry renders as an anchor. */\n href?: string;\n /** Invoked when the entry is activated (click / Enter). */\n onSelect?: () => void;\n /** Nested entries — presence turns the item into a submenu trigger. */\n children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n /** Top-level navigation entries. */\n items: NavigationMenuItem[];\n}\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n * or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n * items={[\n * { label: \"Home\", href: \"/\" },\n * {\n * label: \"Products\",\n * children: [\n * { label: \"Analytics\", href: \"/analytics\" },\n * { label: \"Billing\", onSelect: () => openBilling() },\n * ],\n * },\n * ]}\n * />\n */\nexport function NavigationMenu({ items, className, ...props }: NavigationMenuProps) {\n const [clickedIndex, setClickedIndex] = useState<number>(-1);\n const [hovered, setHovered] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLElement>(null);\n\n const close = useCallback((): void => {\n setClickedIndex(-1);\n setHovered(-1);\n }, []);\n\n const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\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, close]);\n\n const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n const handleClick = (): void => {\n item.onSelect?.();\n close();\n };\n if (item.href) {\n return (\n <a\n href={item.href}\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </a>\n );\n }\n return (\n <button\n type=\"button\"\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </button>\n );\n };\n\n return (\n <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n <ul className={styles.list}>\n {items.map((item, index) => {\n const hasChildren = !!item.children && item.children.length > 0;\n if (!hasChildren) {\n return (\n <li key={index} className={styles.item}>\n {renderLeaf(item, \"none\")}\n </li>\n );\n }\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <li\n key={index}\n className={styles.item}\n onMouseEnter={() => setHovered(index)}\n onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() =>\n setClickedIndex((prev) => (prev === index ? -1 : index))\n }\n onFocus={() => setHovered(index)}\n >\n {item.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {item.children!.map((child, childIndex) => (\n <li key={childIndex} role=\"none\" className={styles.subItem}>\n {renderLeaf(child, \"menuitem\")}\n </li>\n ))}\n </ul>\n )}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n}\n"],"mappings":"qIAgDA,SAAgB,EAAe,CAAE,QAAO,YAAW,GAAG,GAA8B,CAChF,GAAM,CAAC,EAAc,IAAA,EAAA,EAAA,SAAA,CAAoC,EAAE,EACrD,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAA+B,EAAE,EAC3C,GAAA,EAAA,EAAA,MAAA,CAAe,EACf,GAAA,EAAA,EAAA,OAAA,CAA8B,IAAI,EAElC,GAAA,EAAA,EAAA,YAAA,KAAgC,CAClC,EAAgB,EAAE,EAClB,EAAW,EAAE,CACjB,EAAG,CAAC,CAAC,EAEC,EAAY,IAAiB,GAAoB,EAAf,GAExC,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAc,GAAI,OACtB,IAAM,EAAS,GAA+B,CACtC,EAAM,MAAQ,UAAU,EAAM,CACtC,EACM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GAAG,EAAM,CAClF,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAK,EACxC,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,UAAW,CAAK,EAC3C,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAW,CAAK,CAAC,EAErB,IAAM,GAAc,EAA0B,IAAyC,CACnF,IAAM,MAA0B,CAC5B,EAAK,WAAW,EAChB,EAAM,CACV,EAaA,OAZI,EAAK,MAED,EAAA,EAAA,IAAA,CAAC,IAAD,CACI,KAAM,EAAK,KACX,KAAM,IAAS,WAAa,WAAa,IAAA,GACzC,UAAW,EAAA,QAAO,KAClB,QAAS,WAER,EAAK,KACP,CAAA,GAIP,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAM,IAAS,WAAa,WAAa,IAAA,GACzC,UAAW,EAAA,QAAO,KAClB,QAAS,WAER,EAAK,KACF,CAAA,CAEhB,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAS,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,GAAI,YAC1D,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,cACjB,EAAM,KAAK,EAAM,IAAU,CAExB,GAAI,EADkB,EAAK,UAAY,EAAK,SAAS,OAAS,GAE1D,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAgB,UAAW,EAAA,QAAO,cAC7B,EAAW,EAAM,MAAM,CACxB,EAFK,CAEL,EAGZ,IAAM,EAAO,IAAc,EACrB,EAAU,GAAG,EAAO,SAAS,IACnC,OACI,EAAA,EAAA,KAAA,CAAC,KAAD,CAEI,UAAW,EAAA,QAAO,KAClB,iBAAoB,EAAW,CAAK,EACpC,iBAAoB,EAAY,GAAU,IAAS,EAAQ,GAAK,CAAK,WAJzE,EAMI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,gBAAc,OACd,gBAAe,EACf,gBAAe,EACf,YACI,EAAiB,GAAU,IAAS,EAAQ,GAAK,CAAM,EAE3D,YAAe,EAAW,CAAK,WAE9B,EAAK,KACF,CAAA,EACP,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,KAAK,OAAO,UAAW,EAAA,QAAO,eAC1C,EAAK,SAAU,KAAK,EAAO,KACxB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAqB,KAAK,OAAO,UAAW,EAAA,QAAO,iBAC9C,EAAW,EAAO,UAAU,CAC7B,EAFK,CAEL,CACP,CACD,CAAA,CAER,GA3BK,CA2BL,CAEZ,CAAC,CACD,CAAA,CACH,CAAA,CAEb"}
1
+ {"version":3,"file":"NavigationMenu.cjs","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n /** Visible label for the entry. */\n label: ReactNode;\n /** When set, the entry renders as an anchor. */\n href?: string;\n /** Invoked when the entry is activated (click / Enter). */\n onSelect?: () => void;\n /** Nested entries — presence turns the item into a submenu trigger. */\n children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n /** Top-level navigation entries. */\n items: NavigationMenuItem[];\n}\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n * or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n * items={[\n * { label: \"Home\", href: \"/\" },\n * {\n * label: \"Products\",\n * children: [\n * { label: \"Analytics\", href: \"/analytics\" },\n * { label: \"Billing\", onSelect: () => openBilling() },\n * ],\n * },\n * ]}\n * />\n */\nexport function NavigationMenu({ items, className, ...props }: NavigationMenuProps) {\n const [clickedIndex, setClickedIndex] = useState<number>(-1);\n const [hovered, setHovered] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLElement>(null);\n\n const close = useCallback((): void => {\n setClickedIndex(-1);\n setHovered(-1);\n }, []);\n\n const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\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, close]);\n\n const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n const handleClick = (): void => {\n item.onSelect?.();\n close();\n };\n if (item.href) {\n return (\n <a\n href={item.href}\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </a>\n );\n }\n return (\n <button\n type=\"button\"\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </button>\n );\n };\n\n return (\n <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n <ul className={styles.list}>\n {items.map((item, index) => {\n const hasChildren = !!item.children && item.children.length > 0;\n if (!hasChildren) {\n return (\n <li key={index} className={styles.item}>\n {renderLeaf(item, \"none\")}\n </li>\n );\n }\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <li\n key={index}\n className={styles.item}\n onMouseEnter={() => setHovered(index)}\n onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() =>\n setClickedIndex((prev) => (prev === index ? -1 : index))\n }\n onFocus={() => setHovered(index)}\n >\n {item.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {item.children!.map((child, childIndex) => (\n <li key={childIndex} role=\"none\" className={styles.subItem}>\n {renderLeaf(child, \"menuitem\")}\n </li>\n ))}\n </ul>\n )}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n}\n"],"mappings":"qIAgDA,SAAgB,EAAe,CAAE,QAAO,YAAW,GAAG,GAA8B,CAChF,GAAM,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAAiB,EAAE,EACrD,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAiB,EAAE,EAC3C,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,GAAA,EAAU,EAAA,OAAA,CAAoB,IAAI,EAElC,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAgB,EAAE,EAClB,EAAW,EAAE,CACjB,EAAG,CAAC,CAAC,EAEC,EAAY,IAAiB,GAAoB,EAAf,GAExC,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,IAAc,GAAI,OACtB,IAAM,EAAS,GAA+B,CACtC,EAAM,MAAQ,UAAU,EAAM,CACtC,EACM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GAAG,EAAM,CAClF,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAK,EACxC,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,UAAW,CAAK,EAC3C,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAW,CAAK,CAAC,EAErB,IAAM,GAAc,EAA0B,IAAyC,CACnF,IAAM,MAA0B,CAC5B,EAAK,WAAW,EAChB,EAAM,CACV,EAaA,OAZI,EAAK,MAED,EAAA,EAAA,IAAA,CAAC,IAAD,CACI,KAAM,EAAK,KACX,KAAM,IAAS,WAAa,WAAa,IAAA,GACzC,UAAW,EAAA,QAAO,KAClB,QAAS,EAER,SAAA,EAAK,KACP,CAAA,GAIP,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAM,IAAS,WAAa,WAAa,IAAA,GACzC,UAAW,EAAA,QAAO,KAClB,QAAS,EAER,SAAA,EAAK,KACF,CAAA,CAEhB,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAS,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,GAAI,EAC1D,UAAA,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,KACjB,SAAA,EAAM,KAAK,EAAM,IAAU,CAExB,GAAI,EADkB,EAAK,UAAY,EAAK,SAAS,OAAS,GAE1D,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAgB,UAAW,EAAA,QAAO,KAC7B,SAAA,EAAW,EAAM,MAAM,CACxB,EAFK,CAEL,EAGZ,IAAM,EAAO,IAAc,EACrB,EAAU,GAAG,EAAO,SAAS,IACnC,OACI,EAAA,EAAA,KAAA,CAAC,KAAD,CAEI,UAAW,EAAA,QAAO,KAClB,iBAAoB,EAAW,CAAK,EACpC,iBAAoB,EAAY,GAAU,IAAS,EAAQ,GAAK,CAAK,EAJzE,SAAA,EAMI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,gBAAc,OACd,gBAAe,EACf,gBAAe,EACf,YACI,EAAiB,GAAU,IAAS,EAAQ,GAAK,CAAM,EAE3D,YAAe,EAAW,CAAK,EAE9B,SAAA,EAAK,KACF,CAAA,EACP,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,KAAK,OAAO,UAAW,EAAA,QAAO,MAC1C,SAAA,EAAK,SAAU,KAAK,EAAO,KACxB,EAAA,EAAA,IAAA,CAAC,KAAD,CAAqB,KAAK,OAAO,UAAW,EAAA,QAAO,QAC9C,SAAA,EAAW,EAAO,UAAU,CAC7B,EAFK,CAEL,CACP,CACD,CAAA,CAER,CA3BK,EAAA,CA2BL,CAEZ,CAAC,CACD,CAAA,CACH,CAAA,CAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationMenu.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n /** Visible label for the entry. */\n label: ReactNode;\n /** When set, the entry renders as an anchor. */\n href?: string;\n /** Invoked when the entry is activated (click / Enter). */\n onSelect?: () => void;\n /** Nested entries — presence turns the item into a submenu trigger. */\n children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n /** Top-level navigation entries. */\n items: NavigationMenuItem[];\n}\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n * or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n * items={[\n * { label: \"Home\", href: \"/\" },\n * {\n * label: \"Products\",\n * children: [\n * { label: \"Analytics\", href: \"/analytics\" },\n * { label: \"Billing\", onSelect: () => openBilling() },\n * ],\n * },\n * ]}\n * />\n */\nexport function NavigationMenu({ items, className, ...props }: NavigationMenuProps) {\n const [clickedIndex, setClickedIndex] = useState<number>(-1);\n const [hovered, setHovered] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLElement>(null);\n\n const close = useCallback((): void => {\n setClickedIndex(-1);\n setHovered(-1);\n }, []);\n\n const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\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, close]);\n\n const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n const handleClick = (): void => {\n item.onSelect?.();\n close();\n };\n if (item.href) {\n return (\n <a\n href={item.href}\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </a>\n );\n }\n return (\n <button\n type=\"button\"\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </button>\n );\n };\n\n return (\n <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n <ul className={styles.list}>\n {items.map((item, index) => {\n const hasChildren = !!item.children && item.children.length > 0;\n if (!hasChildren) {\n return (\n <li key={index} className={styles.item}>\n {renderLeaf(item, \"none\")}\n </li>\n );\n }\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <li\n key={index}\n className={styles.item}\n onMouseEnter={() => setHovered(index)}\n onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() =>\n setClickedIndex((prev) => (prev === index ? -1 : index))\n }\n onFocus={() => setHovered(index)}\n >\n {item.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {item.children!.map((child, childIndex) => (\n <li key={childIndex} role=\"none\" className={styles.subItem}>\n {renderLeaf(child, \"menuitem\")}\n </li>\n ))}\n </ul>\n )}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n}\n"],"mappings":";;;;;AAgDA,SAAgB,EAAe,EAAE,UAAO,cAAW,GAAG,KAA8B;CAChF,IAAM,CAAC,GAAc,KAAmB,EAAiB,EAAE,GACrD,CAAC,GAAS,KAAc,EAAiB,EAAE,GAC3C,IAAS,EAAM,GACf,IAAU,EAAoB,IAAI,GAElC,IAAQ,QAAwB;EAElC,AADA,EAAgB,EAAE,GAClB,EAAW,EAAE;CACjB,GAAG,CAAC,CAAC,GAEC,IAAY,MAAiB,KAAoB,IAAf;CAExC,QAAgB;EACZ,IAAI,MAAc,IAAI;EACtB,IAAM,KAAS,MAA+B;GAC1C,AAAI,EAAM,QAAQ,YAAU,EAAM;EACtC,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,CAAC,GAAW,CAAK,CAAC;CAErB,IAAM,KAAc,GAA0B,MAAyC;EACnF,IAAM,UAA0B;GAE5B,AADA,EAAK,WAAW,GAChB,EAAM;EACV;EAaA,OAZI,EAAK,OAED,kBAAC,KAAD;GACI,MAAM,EAAK;GACX,MAAM,MAAS,aAAa,aAAa,KAAA;GACzC,WAAW,EAAO;GAClB,SAAS;aAER,EAAK;EACP,CAAA,IAIP,kBAAC,UAAD;GACI,MAAK;GACL,MAAM,MAAS,aAAa,aAAa,KAAA;GACzC,WAAW,EAAO;GAClB,SAAS;aAER,EAAK;EACF,CAAA;CAEhB;CAEA,OACI,kBAAC,OAAD;EAAK,KAAK;EAAS,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;YAC1D,kBAAC,MAAD;GAAI,WAAW,EAAO;aACjB,EAAM,KAAK,GAAM,MAAU;IAExB,IAAI,EADkB,EAAK,YAAY,EAAK,SAAS,SAAS,IAE1D,OACI,kBAAC,MAAD;KAAgB,WAAW,EAAO;eAC7B,EAAW,GAAM,MAAM;IACxB,GAFK,CAEL;IAGZ,IAAM,IAAO,MAAc,GACrB,IAAU,GAAG,EAAO,SAAS;IACnC,OACI,kBAAC,MAAD;KAEI,WAAW,EAAO;KAClB,oBAAoB,EAAW,CAAK;KACpC,oBAAoB,GAAY,MAAU,MAAS,IAAQ,KAAK,CAAK;eAJzE,CAMI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,iBAAc;MACd,iBAAe;MACf,iBAAe;MACf,eACI,GAAiB,MAAU,MAAS,IAAQ,KAAK,CAAM;MAE3D,eAAe,EAAW,CAAK;gBAE9B,EAAK;KACF,CAAA,GACP,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,MAAK;MAAO,WAAW,EAAO;gBAC1C,EAAK,SAAU,KAAK,GAAO,MACxB,kBAAC,MAAD;OAAqB,MAAK;OAAO,WAAW,EAAO;iBAC9C,EAAW,GAAO,UAAU;MAC7B,GAFK,CAEL,CACP;KACD,CAAA,CAER;OA3BK,CA2BL;GAEZ,CAAC;EACD,CAAA;CACH,CAAA;AAEb"}
1
+ {"version":3,"file":"NavigationMenu.js","names":[],"sources":["../../../src/components/NavigationMenu/NavigationMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits function-lines — the body resolves the active item from the\n * current path, owns the open submenu and the hover-intent delay, all of which\n * decide the same highlighted row.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationMenu.module.css\";\n\nexport interface NavigationMenuItem {\n /** Visible label for the entry. */\n label: ReactNode;\n /** When set, the entry renders as an anchor. */\n href?: string;\n /** Invoked when the entry is activated (click / Enter). */\n onSelect?: () => void;\n /** Nested entries — presence turns the item into a submenu trigger. */\n children?: NavigationMenuItem[];\n}\n\nexport interface NavigationMenuProps extends HTMLAttributes<HTMLElement> {\n /** Top-level navigation entries. */\n items: NavigationMenuItem[];\n}\n\n/**\n * Horizontal navigation menu with hover/click/focus dropdown submenus.\n *\n * - Top-level items render in a `<nav><ul>`.\n * - Items with `children` open a submenu panel (`role=\"menu\"`) on hover, focus,\n * or click. Only one panel is open at a time.\n * - Closes on outside click, Escape, or selecting a leaf entry.\n *\n * @example\n * <NavigationMenu\n * items={[\n * { label: \"Home\", href: \"/\" },\n * {\n * label: \"Products\",\n * children: [\n * { label: \"Analytics\", href: \"/analytics\" },\n * { label: \"Billing\", onSelect: () => openBilling() },\n * ],\n * },\n * ]}\n * />\n */\nexport function NavigationMenu({ items, className, ...props }: NavigationMenuProps) {\n const [clickedIndex, setClickedIndex] = useState<number>(-1);\n const [hovered, setHovered] = useState<number>(-1);\n const baseId = useId();\n const rootRef = useRef<HTMLElement>(null);\n\n const close = useCallback((): void => {\n setClickedIndex(-1);\n setHovered(-1);\n }, []);\n\n const openIndex = clickedIndex !== -1 ? clickedIndex : hovered;\n\n useEffect(() => {\n if (openIndex === -1) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\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, close]);\n\n const renderLeaf = (item: NavigationMenuItem, role: \"none\" | \"menuitem\"): ReactNode => {\n const handleClick = (): void => {\n item.onSelect?.();\n close();\n };\n if (item.href) {\n return (\n <a\n href={item.href}\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </a>\n );\n }\n return (\n <button\n type=\"button\"\n role={role === \"menuitem\" ? \"menuitem\" : undefined}\n className={styles.link}\n onClick={handleClick}\n >\n {item.label}\n </button>\n );\n };\n\n return (\n <nav ref={rootRef} className={cn(styles.root, className)} {...props}>\n <ul className={styles.list}>\n {items.map((item, index) => {\n const hasChildren = !!item.children && item.children.length > 0;\n if (!hasChildren) {\n return (\n <li key={index} className={styles.item}>\n {renderLeaf(item, \"none\")}\n </li>\n );\n }\n const open = openIndex === index;\n const panelId = `${baseId}-panel-${index}`;\n return (\n <li\n key={index}\n className={styles.item}\n onMouseEnter={() => setHovered(index)}\n onMouseLeave={() => setHovered((prev) => (prev === index ? -1 : prev))}\n >\n <button\n type=\"button\"\n className={styles.trigger}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-controls={panelId}\n onClick={() =>\n setClickedIndex((prev) => (prev === index ? -1 : index))\n }\n onFocus={() => setHovered(index)}\n >\n {item.label}\n </button>\n {open && (\n <ul id={panelId} role=\"menu\" className={styles.panel}>\n {item.children!.map((child, childIndex) => (\n <li key={childIndex} role=\"none\" className={styles.subItem}>\n {renderLeaf(child, \"menuitem\")}\n </li>\n ))}\n </ul>\n )}\n </li>\n );\n })}\n </ul>\n </nav>\n );\n}\n"],"mappings":";;;;;AAgDA,SAAgB,EAAe,EAAE,UAAO,cAAW,GAAG,KAA8B;CAChF,IAAM,CAAC,GAAc,KAAmB,EAAiB,EAAE,GACrD,CAAC,GAAS,KAAc,EAAiB,EAAE,GAC3C,IAAS,EAAM,GACf,IAAU,EAAoB,IAAI,GAElC,IAAQ,QAAwB;EAElC,AADA,EAAgB,EAAE,GAClB,EAAW,EAAE;CACjB,GAAG,CAAC,CAAC,GAEC,IAAY,MAAiB,KAAoB,IAAf;CAExC,QAAgB;EACZ,IAAI,MAAc,IAAI;EACtB,IAAM,KAAS,MAA+B;GAC1C,AAAI,EAAM,QAAQ,YAAU,EAAM;EACtC,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,CAAC,GAAW,CAAK,CAAC;CAErB,IAAM,KAAc,GAA0B,MAAyC;EACnF,IAAM,UAA0B;GAE5B,AADA,EAAK,WAAW,GAChB,EAAM;EACV;EAaA,OAZI,EAAK,OAED,kBAAC,KAAD;GACI,MAAM,EAAK;GACX,MAAM,MAAS,aAAa,aAAa,KAAA;GACzC,WAAW,EAAO;GAClB,SAAS;GAER,UAAA,EAAK;EACP,CAAA,IAIP,kBAAC,UAAD;GACI,MAAK;GACL,MAAM,MAAS,aAAa,aAAa,KAAA;GACzC,WAAW,EAAO;GAClB,SAAS;GAER,UAAA,EAAK;EACF,CAAA;CAEhB;CAEA,OACI,kBAAC,OAAD;EAAK,KAAK;EAAS,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;EAC1D,UAAA,kBAAC,MAAD;GAAI,WAAW,EAAO;GACjB,UAAA,EAAM,KAAK,GAAM,MAAU;IAExB,IAAI,EADkB,EAAK,YAAY,EAAK,SAAS,SAAS,IAE1D,OACI,kBAAC,MAAD;KAAgB,WAAW,EAAO;KAC7B,UAAA,EAAW,GAAM,MAAM;IACxB,GAFK,CAEL;IAGZ,IAAM,IAAO,MAAc,GACrB,IAAU,GAAG,EAAO,SAAS;IACnC,OACI,kBAAC,MAAD;KAEI,WAAW,EAAO;KAClB,oBAAoB,EAAW,CAAK;KACpC,oBAAoB,GAAY,MAAU,MAAS,IAAQ,KAAK,CAAK;KAJzE,UAAA,CAMI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,iBAAc;MACd,iBAAe;MACf,iBAAe;MACf,eACI,GAAiB,MAAU,MAAS,IAAQ,KAAK,CAAM;MAE3D,eAAe,EAAW,CAAK;MAE9B,UAAA,EAAK;KACF,CAAA,GACP,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,MAAK;MAAO,WAAW,EAAO;MAC1C,UAAA,EAAK,SAAU,KAAK,GAAO,MACxB,kBAAC,MAAD;OAAqB,MAAK;OAAO,WAAW,EAAO;OAC9C,UAAA,EAAW,GAAO,UAAU;MAC7B,GAFK,CAEL,CACP;KACD,CAAA,CAER;IA3BK,GAAA,CA2BL;GAEZ,CAAC;EACD,CAAA;CACH,CAAA;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRail.cjs","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationRail.module.css\";\n\nexport interface NavigationRailItem {\n /** Unique identifier — used as React key and for value matching. */\n key: string;\n /** Visible label. */\n label: ReactNode;\n /** Icon rendered above the label. */\n icon?: ReactNode;\n /** Optional badge content rendered over the icon. */\n badge?: ReactNode;\n /** When true, the item is not selectable. */\n disabled?: boolean;\n}\n\nexport type NavigationRailLabelVisibility = \"all\" | \"selected\" | \"none\";\n\nexport interface NavigationRailProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n items: NavigationRailItem[];\n /** Selected key. */\n value: string;\n /** Called with the new selected key on click. */\n onChange: (key: string) => void;\n /** Top slot — e.g. a FAB or logo. */\n header?: ReactNode;\n /** Bottom slot — pushed to the bottom of the rail. */\n footer?: ReactNode;\n /** Which item labels to show. Default `\"all\"`. */\n labelVisibility?: NavigationRailLabelVisibility;\n /** Extra class names merged onto the root. */\n className?: string;\n}\n\n/**\n * Vertical, compact navigation column for desktop and tablet layouts. Each item\n * stacks an icon over its label, with the active item flagged via\n * `aria-current=\"page\"`. Use `labelVisibility=\"selected\"` to show only the\n * active item's label, or `\"none\"` for an icon-only rail.\n *\n * @example\n * <NavigationRail\n * header={<FloatingActionButton icon={<Plus />} position=\"none\" />}\n * items={[\n * { key: \"home\", label: \"Início\", icon: <Home /> },\n * { key: \"inbox\", label: \"Caixa\", icon: <Inbox />, badge: 3 },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function NavigationRail({\n items,\n value,\n onChange,\n header,\n footer,\n labelVisibility = \"all\",\n className,\n ...props\n}: NavigationRailProps) {\n return (\n <nav className={cn(styles.rail, className)} aria-label=\"Navegação lateral\" {...props}>\n {header !== undefined && <div className={styles.header}>{header}</div>}\n <ul className={styles.list}>\n {items.map((item) => {\n const active = item.key === value;\n const showLabel =\n labelVisibility === \"all\" || (labelVisibility === \"selected\" && active);\n return (\n <li key={item.key}>\n <button\n type=\"button\"\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n disabled={item.disabled}\n onClick={() => onChange(item.key)}\n >\n <span className={styles.iconWrap}>\n {item.icon && <span className={styles.icon}>{item.icon}</span>}\n {item.badge !== undefined && (\n <span className={styles.badge}>{item.badge}</span>\n )}\n </span>\n {showLabel && <span className={styles.label}>{item.label}</span>}\n </button>\n </li>\n );\n })}\n </ul>\n {footer !== undefined && <div className={styles.footer}>{footer}</div>}\n </nav>\n );\n}\n"],"mappings":"kHAoDA,SAAgB,EAAe,CAC3B,QACA,QACA,WACA,SACA,SACA,kBAAkB,MAClB,YACA,GAAG,GACiB,CACpB,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,aAAW,oBAAoB,GAAI,WAA/E,CACK,IAAW,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,gBAAS,CAAY,CAAA,GACrE,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,cACjB,EAAM,IAAK,GAAS,CACjB,IAAM,EAAS,EAAK,MAAQ,EACtB,EACF,IAAoB,OAAU,IAAoB,YAAc,EACpE,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAA,UACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,GAAU,EAAA,QAAO,MAAM,EAClD,eAAc,EAAS,OAAS,IAAA,GAChC,SAAU,EAAK,SACf,YAAe,EAAS,EAAK,GAAG,WALpC,EAOI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,kBAAxB,CACK,EAAK,OAAQ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,cAAO,EAAK,IAAW,CAAA,EAC5D,EAAK,QAAU,IAAA,KACZ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,EAAK,KAAY,CAAA,CAEnD,IACL,IAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,eAAQ,EAAK,KAAY,CAAA,CAC3D,GACR,EAhBK,EAAK,GAgBV,CAEZ,CAAC,CACD,CAAA,EACH,IAAW,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,gBAAS,CAAY,CAAA,CACpE,GAEb"}
1
+ {"version":3,"file":"NavigationRail.cjs","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationRail.module.css\";\n\nexport interface NavigationRailItem {\n /** Unique identifier — used as React key and for value matching. */\n key: string;\n /** Visible label. */\n label: ReactNode;\n /** Icon rendered above the label. */\n icon?: ReactNode;\n /** Optional badge content rendered over the icon. */\n badge?: ReactNode;\n /** When true, the item is not selectable. */\n disabled?: boolean;\n}\n\nexport type NavigationRailLabelVisibility = \"all\" | \"selected\" | \"none\";\n\nexport interface NavigationRailProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n items: NavigationRailItem[];\n /** Selected key. */\n value: string;\n /** Called with the new selected key on click. */\n onChange: (key: string) => void;\n /** Top slot — e.g. a FAB or logo. */\n header?: ReactNode;\n /** Bottom slot — pushed to the bottom of the rail. */\n footer?: ReactNode;\n /** Which item labels to show. Default `\"all\"`. */\n labelVisibility?: NavigationRailLabelVisibility;\n /** Extra class names merged onto the root. */\n className?: string;\n}\n\n/**\n * Vertical, compact navigation column for desktop and tablet layouts. Each item\n * stacks an icon over its label, with the active item flagged via\n * `aria-current=\"page\"`. Use `labelVisibility=\"selected\"` to show only the\n * active item's label, or `\"none\"` for an icon-only rail.\n *\n * @example\n * <NavigationRail\n * header={<FloatingActionButton icon={<Plus />} position=\"none\" />}\n * items={[\n * { key: \"home\", label: \"Início\", icon: <Home /> },\n * { key: \"inbox\", label: \"Caixa\", icon: <Inbox />, badge: 3 },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function NavigationRail({\n items,\n value,\n onChange,\n header,\n footer,\n labelVisibility = \"all\",\n className,\n ...props\n}: NavigationRailProps) {\n return (\n <nav className={cn(styles.rail, className)} aria-label=\"Navegação lateral\" {...props}>\n {header !== undefined && <div className={styles.header}>{header}</div>}\n <ul className={styles.list}>\n {items.map((item) => {\n const active = item.key === value;\n const showLabel =\n labelVisibility === \"all\" || (labelVisibility === \"selected\" && active);\n return (\n <li key={item.key}>\n <button\n type=\"button\"\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n disabled={item.disabled}\n onClick={() => onChange(item.key)}\n >\n <span className={styles.iconWrap}>\n {item.icon && <span className={styles.icon}>{item.icon}</span>}\n {item.badge !== undefined && (\n <span className={styles.badge}>{item.badge}</span>\n )}\n </span>\n {showLabel && <span className={styles.label}>{item.label}</span>}\n </button>\n </li>\n );\n })}\n </ul>\n {footer !== undefined && <div className={styles.footer}>{footer}</div>}\n </nav>\n );\n}\n"],"mappings":"kHAoDA,SAAgB,EAAe,CAC3B,QACA,QACA,WACA,SACA,SACA,kBAAkB,MAClB,YACA,GAAG,GACiB,CACpB,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,aAAW,oBAAoB,GAAI,EAA/E,SAAA,CACK,IAAW,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAS,SAAA,CAAY,CAAA,GACrE,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,KACjB,SAAA,EAAM,IAAK,GAAS,CACjB,IAAM,EAAS,EAAK,MAAQ,EACtB,EACF,IAAoB,OAAU,IAAoB,YAAc,EACpE,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAA,UACI,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,GAAU,EAAA,QAAO,MAAM,EAClD,eAAc,EAAS,OAAS,IAAA,GAChC,SAAU,EAAK,SACf,YAAe,EAAS,EAAK,GAAG,EALpC,SAAA,EAOI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SAAxB,SAAA,CACK,EAAK,OAAQ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,KAAO,SAAA,EAAK,IAAW,CAAA,EAC5D,EAAK,QAAU,IAAA,KACZ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAQ,SAAA,EAAK,KAAY,CAAA,CAEnD,CACL,CAAA,EAAA,IAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MAAQ,SAAA,EAAK,KAAY,CAAA,CAC3D,CACR,CAAA,CAAA,EAhBK,EAAK,GAgBV,CAEZ,CAAC,CACD,CAAA,EACH,IAAW,IAAA,KAAa,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAS,SAAA,CAAY,CAAA,CACpE,GAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationRail.js","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationRail.module.css\";\n\nexport interface NavigationRailItem {\n /** Unique identifier — used as React key and for value matching. */\n key: string;\n /** Visible label. */\n label: ReactNode;\n /** Icon rendered above the label. */\n icon?: ReactNode;\n /** Optional badge content rendered over the icon. */\n badge?: ReactNode;\n /** When true, the item is not selectable. */\n disabled?: boolean;\n}\n\nexport type NavigationRailLabelVisibility = \"all\" | \"selected\" | \"none\";\n\nexport interface NavigationRailProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n items: NavigationRailItem[];\n /** Selected key. */\n value: string;\n /** Called with the new selected key on click. */\n onChange: (key: string) => void;\n /** Top slot — e.g. a FAB or logo. */\n header?: ReactNode;\n /** Bottom slot — pushed to the bottom of the rail. */\n footer?: ReactNode;\n /** Which item labels to show. Default `\"all\"`. */\n labelVisibility?: NavigationRailLabelVisibility;\n /** Extra class names merged onto the root. */\n className?: string;\n}\n\n/**\n * Vertical, compact navigation column for desktop and tablet layouts. Each item\n * stacks an icon over its label, with the active item flagged via\n * `aria-current=\"page\"`. Use `labelVisibility=\"selected\"` to show only the\n * active item's label, or `\"none\"` for an icon-only rail.\n *\n * @example\n * <NavigationRail\n * header={<FloatingActionButton icon={<Plus />} position=\"none\" />}\n * items={[\n * { key: \"home\", label: \"Início\", icon: <Home /> },\n * { key: \"inbox\", label: \"Caixa\", icon: <Inbox />, badge: 3 },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function NavigationRail({\n items,\n value,\n onChange,\n header,\n footer,\n labelVisibility = \"all\",\n className,\n ...props\n}: NavigationRailProps) {\n return (\n <nav className={cn(styles.rail, className)} aria-label=\"Navegação lateral\" {...props}>\n {header !== undefined && <div className={styles.header}>{header}</div>}\n <ul className={styles.list}>\n {items.map((item) => {\n const active = item.key === value;\n const showLabel =\n labelVisibility === \"all\" || (labelVisibility === \"selected\" && active);\n return (\n <li key={item.key}>\n <button\n type=\"button\"\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n disabled={item.disabled}\n onClick={() => onChange(item.key)}\n >\n <span className={styles.iconWrap}>\n {item.icon && <span className={styles.icon}>{item.icon}</span>}\n {item.badge !== undefined && (\n <span className={styles.badge}>{item.badge}</span>\n )}\n </span>\n {showLabel && <span className={styles.label}>{item.label}</span>}\n </button>\n </li>\n );\n })}\n </ul>\n {footer !== undefined && <div className={styles.footer}>{footer}</div>}\n </nav>\n );\n}\n"],"mappings":";;;;AAoDA,SAAgB,EAAe,EAC3B,UACA,UACA,aACA,WACA,WACA,qBAAkB,OAClB,cACA,GAAG,KACiB;CACpB,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,cAAW;EAAoB,GAAI;YAA/E;GACK,MAAW,KAAA,KAAa,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAS;GAAY,CAAA;GACrE,kBAAC,MAAD;IAAI,WAAW,EAAO;cACjB,EAAM,KAAK,MAAS;KACjB,IAAM,IAAS,EAAK,QAAQ,GACtB,IACF,MAAoB,SAAU,MAAoB,cAAc;KACpE,OACI,kBAAC,MAAD,EAAA,UACI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAG,EAAO,MAAM,KAAU,EAAO,MAAM;MAClD,gBAAc,IAAS,SAAS,KAAA;MAChC,UAAU,EAAK;MACf,eAAe,EAAS,EAAK,GAAG;gBALpC,CAOI,kBAAC,QAAD;OAAM,WAAW,EAAO;iBAAxB,CACK,EAAK,QAAQ,kBAAC,QAAD;QAAM,WAAW,EAAO;kBAAO,EAAK;OAAW,CAAA,GAC5D,EAAK,UAAU,KAAA,KACZ,kBAAC,QAAD;QAAM,WAAW,EAAO;kBAAQ,EAAK;OAAY,CAAA,CAEnD;UACL,KAAa,kBAAC,QAAD;OAAM,WAAW,EAAO;iBAAQ,EAAK;MAAY,CAAA,CAC3D;QACR,GAhBK,EAAK,GAgBV;IAEZ,CAAC;GACD,CAAA;GACH,MAAW,KAAA,KAAa,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAS;GAAY,CAAA;EACpE;;AAEb"}
1
+ {"version":3,"file":"NavigationRail.js","names":[],"sources":["../../../src/components/NavigationRail/NavigationRail.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./NavigationRail.module.css\";\n\nexport interface NavigationRailItem {\n /** Unique identifier — used as React key and for value matching. */\n key: string;\n /** Visible label. */\n label: ReactNode;\n /** Icon rendered above the label. */\n icon?: ReactNode;\n /** Optional badge content rendered over the icon. */\n badge?: ReactNode;\n /** When true, the item is not selectable. */\n disabled?: boolean;\n}\n\nexport type NavigationRailLabelVisibility = \"all\" | \"selected\" | \"none\";\n\nexport interface NavigationRailProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\n items: NavigationRailItem[];\n /** Selected key. */\n value: string;\n /** Called with the new selected key on click. */\n onChange: (key: string) => void;\n /** Top slot — e.g. a FAB or logo. */\n header?: ReactNode;\n /** Bottom slot — pushed to the bottom of the rail. */\n footer?: ReactNode;\n /** Which item labels to show. Default `\"all\"`. */\n labelVisibility?: NavigationRailLabelVisibility;\n /** Extra class names merged onto the root. */\n className?: string;\n}\n\n/**\n * Vertical, compact navigation column for desktop and tablet layouts. Each item\n * stacks an icon over its label, with the active item flagged via\n * `aria-current=\"page\"`. Use `labelVisibility=\"selected\"` to show only the\n * active item's label, or `\"none\"` for an icon-only rail.\n *\n * @example\n * <NavigationRail\n * header={<FloatingActionButton icon={<Plus />} position=\"none\" />}\n * items={[\n * { key: \"home\", label: \"Início\", icon: <Home /> },\n * { key: \"inbox\", label: \"Caixa\", icon: <Inbox />, badge: 3 },\n * ]}\n * value={tab}\n * onChange={setTab}\n * />\n */\nexport function NavigationRail({\n items,\n value,\n onChange,\n header,\n footer,\n labelVisibility = \"all\",\n className,\n ...props\n}: NavigationRailProps) {\n return (\n <nav className={cn(styles.rail, className)} aria-label=\"Navegação lateral\" {...props}>\n {header !== undefined && <div className={styles.header}>{header}</div>}\n <ul className={styles.list}>\n {items.map((item) => {\n const active = item.key === value;\n const showLabel =\n labelVisibility === \"all\" || (labelVisibility === \"selected\" && active);\n return (\n <li key={item.key}>\n <button\n type=\"button\"\n className={cn(styles.item, active && styles.active)}\n aria-current={active ? \"page\" : undefined}\n disabled={item.disabled}\n onClick={() => onChange(item.key)}\n >\n <span className={styles.iconWrap}>\n {item.icon && <span className={styles.icon}>{item.icon}</span>}\n {item.badge !== undefined && (\n <span className={styles.badge}>{item.badge}</span>\n )}\n </span>\n {showLabel && <span className={styles.label}>{item.label}</span>}\n </button>\n </li>\n );\n })}\n </ul>\n {footer !== undefined && <div className={styles.footer}>{footer}</div>}\n </nav>\n );\n}\n"],"mappings":";;;;AAoDA,SAAgB,EAAe,EAC3B,UACA,UACA,aACA,WACA,WACA,qBAAkB,OAClB,cACA,GAAG,KACiB;CACpB,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,cAAW;EAAoB,GAAI;EAA/E,UAAA;GACK,MAAW,KAAA,KAAa,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAS,UAAA;GAAY,CAAA;GACrE,kBAAC,MAAD;IAAI,WAAW,EAAO;IACjB,UAAA,EAAM,KAAK,MAAS;KACjB,IAAM,IAAS,EAAK,QAAQ,GACtB,IACF,MAAoB,SAAU,MAAoB,cAAc;KACpE,OACI,kBAAC,MAAD,EAAA,UACI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAG,EAAO,MAAM,KAAU,EAAO,MAAM;MAClD,gBAAc,IAAS,SAAS,KAAA;MAChC,UAAU,EAAK;MACf,eAAe,EAAS,EAAK,GAAG;MALpC,UAAA,CAOI,kBAAC,QAAD;OAAM,WAAW,EAAO;OAAxB,UAAA,CACK,EAAK,QAAQ,kBAAC,QAAD;QAAM,WAAW,EAAO;QAAO,UAAA,EAAK;OAAW,CAAA,GAC5D,EAAK,UAAU,KAAA,KACZ,kBAAC,QAAD;QAAM,WAAW,EAAO;QAAQ,UAAA,EAAK;OAAY,CAAA,CAEnD;MACL,CAAA,GAAA,KAAa,kBAAC,QAAD;OAAM,WAAW,EAAO;OAAQ,UAAA,EAAK;MAAY,CAAA,CAC3D;KACR,CAAA,EAAA,GAhBK,EAAK,GAgBV;IAEZ,CAAC;GACD,CAAA;GACH,MAAW,KAAA,KAAa,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAS,UAAA;GAAY,CAAA;EACpE;;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationCenter.cjs","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an inbox has four actions the app\n * must implement (onSelect, onMarkRead, onMarkAllRead, onDismiss) over one list,\n * plus the presentation it needs to render a readable feed: renderIcon, locale, now\n * for relative timestamps, emptyState and title.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { relativeTime, type RelativeTimeLocale } from \"@/utils/relative-time\";\n\nimport { Badge } from \"../Badge\";\nimport { EmptyState } from \"../EmptyState\";\nimport type { NotificationItem } from \"./use-notification-inbox\";\nimport styles from \"./NotificationCenter.module.css\";\n\n/**\n * DOM attributes this component redefines.\n *\n * `title` becomes a rendered heading node instead of a tooltip string, and\n * `onSelect` becomes \"an entry was activated\" instead of the DOM text-selection\n * event. Both would be type errors if merged, and silently confusing if allowed\n * through.\n */\ntype OverriddenDomProps = \"children\" | \"title\" | \"onSelect\";\n\nexport interface NotificationCenterProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /** Entries to show. Rendered in the order given — `useNotificationInbox` sorts newest first. */\n items: readonly NotificationItem[];\n /**\n * Panel heading. Pass `null` to render the list with no header.\n *\n * Widened from the DOM `title` attribute (a `string`) on purpose: this is a\n * rendered heading, not a tooltip, so it takes a node.\n */\n title?: ReactNode;\n /** Activate an entry: click, `Enter` or `Space`. Also marks it read when `onMarkRead` is set. */\n onSelect?: (item: NotificationItem) => void;\n /** Mark one entry as read. Enables the per-item read control. */\n onMarkRead?: (id: string) => void;\n /** Mark every entry as read. Enables the header action. */\n onMarkAllRead?: () => void;\n /** Remove one entry. Enables the per-item dismiss control. */\n onDismiss?: (id: string) => void;\n /** Icon rendered at the leading edge of each row — an `<Icon name={…} />`, say. */\n renderIcon?: (item: NotificationItem) => ReactNode;\n /** Locale for the relative timestamps. Default `\"pt-BR\"`. */\n locale?: RelativeTimeLocale;\n /** Shown when `items` is empty. */\n emptyState?: ReactNode;\n /** Reference point for the relative timestamps. Default: now, at render time. */\n now?: number;\n}\n\n/**\n * Inbox panel for notifications the app has received.\n *\n * Deliberately presentational and controlled: it takes a list and emits intent.\n * Where the list comes from — an API, a service-worker push, `localStorage` — and\n * where read state is written are app decisions, and a component that assumed any\n * of them would be wrong for most apps. `useNotificationInbox` is the companion\n * that holds the state, including the service-worker bridge.\n *\n * It is also just the panel, not a popover: apps mount it inside their own\n * `Popover`, `Drawer` or route. That keeps one component from owning both the\n * inbox and a positioning strategy.\n *\n * @example\n * const inbox = useNotificationInbox();\n *\n * <Popover trigger={<Button>Notificações ({inbox.unreadCount})</Button>}>\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * onSelect={(item) => item.url && navigate(item.url)}\n * />\n * </Popover>\n */\nexport function NotificationCenter({\n items,\n title = \"Notificações\",\n onSelect,\n onMarkRead,\n onMarkAllRead,\n onDismiss,\n renderIcon,\n locale = \"pt-BR\",\n emptyState,\n now,\n className,\n ...rest\n}: NotificationCenterProps) {\n const unreadCount = items.filter((item) => !item.read).length;\n const reference = now ?? new Date().getTime();\n\n /**\n * Activate an entry.\n *\n * Reading a notification is implied by opening it, so activation marks it read\n * as well — otherwise every app would have to remember to call both, and the\n * unread badge would keep counting something the user already saw.\n */\n const activate = (item: NotificationItem): void => {\n if (!item.read) onMarkRead?.(item.id);\n onSelect?.(item);\n };\n\n const interactive = Boolean(onSelect || onMarkRead);\n\n return (\n <div className={cn(styles.panel, className)} {...rest}>\n {(title || (onMarkAllRead && unreadCount > 0)) && (\n <header className={styles.header}>\n {title ? (\n <h2 className={styles.title}>\n {title}\n {unreadCount > 0 && (\n <Badge variant=\"primary\" aria-label={`${unreadCount} não lidas`}>\n {unreadCount}\n </Badge>\n )}\n </h2>\n ) : (\n <span />\n )}\n {onMarkAllRead && unreadCount > 0 && (\n <button\n type=\"button\"\n className={styles.headerAction}\n onClick={onMarkAllRead}\n >\n Marcar todas como lidas\n </button>\n )}\n </header>\n )}\n\n {items.length === 0 ? (\n (emptyState ?? <EmptyState title=\"Nenhuma notificação\" />)\n ) : (\n <ul className={styles.list}>\n {items.map((item) => (\n <li\n key={item.id}\n className={cn(styles.item, !item.read && styles.unread)}\n aria-current={!item.read ? \"true\" : undefined}\n >\n {renderIcon && <span className={styles.icon}>{renderIcon(item)}</span>}\n\n <div className={styles.body}>\n {interactive ? (\n <button\n type=\"button\"\n className={styles.trigger}\n onClick={() => activate(item)}\n >\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </button>\n ) : (\n <>\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </>\n )}\n <time\n className={styles.time}\n dateTime={new Date(item.receivedAt).toISOString()}\n >\n {relativeTime(item.receivedAt, { locale, now: reference })}\n </time>\n </div>\n\n {onDismiss && (\n <button\n type=\"button\"\n className={styles.dismiss}\n onClick={() => onDismiss(item.id)}\n aria-label={`Descartar: ${item.title}`}\n >\n ×\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"2OAmFA,SAAgB,EAAmB,CAC/B,QACA,QAAQ,eACR,WACA,aACA,gBACA,YACA,aACA,SAAS,QACT,aACA,MACA,YACA,GAAG,GACqB,CACxB,IAAM,EAAc,EAAM,OAAQ,GAAS,CAAC,EAAK,IAAI,CAAC,CAAC,OACjD,EAAY,GAAO,IAAI,KAAK,CAAA,CAAE,QAAQ,EAStC,EAAY,GAAiC,CAC1C,EAAK,MAAM,IAAa,EAAK,EAAE,EACpC,IAAW,CAAI,CACnB,EAEM,EAAc,GAAQ,GAAY,GAExC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,CAAS,EAAG,GAAI,WAAjD,EACM,GAAU,GAAiB,EAAc,KACvC,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,gBAA1B,CACK,GACG,EAAA,EAAA,KAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,eAAtB,CACK,EACA,EAAc,IACX,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAAO,QAAQ,UAAU,aAAY,GAAG,EAAY,qBAC/C,CACE,CAAA,CAEX,KAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAO,CAAA,EAEV,GAAiB,EAAc,IAC5B,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,aAClB,QAAS,WACZ,yBAEO,CAAA,CAER,IAGX,EAAM,SAAW,EACb,IAAc,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAM,qBAAuB,CAAA,GAExD,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,cACjB,EAAM,IAAK,IACR,EAAA,EAAA,KAAA,CAAC,KAAD,CAEI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAC,EAAK,MAAQ,EAAA,QAAO,MAAM,EACtD,eAAe,EAAK,KAAgB,IAAA,GAAT,gBAH/B,CAKK,IAAc,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,cAAO,EAAW,CAAI,CAAQ,CAAA,GAErE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,cAAvB,CACK,GACG,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,YAAe,EAAS,CAAI,WAHhC,EAKI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,mBAAY,EAAK,KAAY,CAAA,EACpD,EAAK,OACF,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,kBAAW,EAAK,IAAW,CAAA,CAEnD,KAER,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,mBAAY,EAAK,KAAY,CAAA,EACpD,EAAK,OACF,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,kBAAW,EAAK,IAAW,CAAA,CAEzD,CAAA,CAAA,GAEN,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,UAAW,EAAA,QAAO,KAClB,SAAU,IAAI,KAAK,EAAK,UAAU,CAAC,CAAC,YAAY,WAE/C,EAAA,aAAa,EAAK,WAAY,CAAE,SAAQ,IAAK,CAAU,CAAC,CACvD,CAAA,CACL,IAEJ,IACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,YAAe,EAAU,EAAK,EAAE,EAChC,aAAY,cAAc,EAAK,iBAClC,GAEO,CAAA,CAEZ,GA5CK,EAAK,EA4CV,CACP,CACD,CAAA,CAEP,GAEb"}
1
+ {"version":3,"file":"NotificationCenter.cjs","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an inbox has four actions the app\n * must implement (onSelect, onMarkRead, onMarkAllRead, onDismiss) over one list,\n * plus the presentation it needs to render a readable feed: renderIcon, locale, now\n * for relative timestamps, emptyState and title.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { relativeTime, type RelativeTimeLocale } from \"@/utils/relative-time\";\n\nimport { Badge } from \"../Badge\";\nimport { EmptyState } from \"../EmptyState\";\nimport type { NotificationItem } from \"./use-notification-inbox\";\nimport styles from \"./NotificationCenter.module.css\";\n\n/**\n * DOM attributes this component redefines.\n *\n * `title` becomes a rendered heading node instead of a tooltip string, and\n * `onSelect` becomes \"an entry was activated\" instead of the DOM text-selection\n * event. Both would be type errors if merged, and silently confusing if allowed\n * through.\n */\ntype OverriddenDomProps = \"children\" | \"title\" | \"onSelect\";\n\nexport interface NotificationCenterProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /** Entries to show. Rendered in the order given — `useNotificationInbox` sorts newest first. */\n items: readonly NotificationItem[];\n /**\n * Panel heading. Pass `null` to render the list with no header.\n *\n * Widened from the DOM `title` attribute (a `string`) on purpose: this is a\n * rendered heading, not a tooltip, so it takes a node.\n */\n title?: ReactNode;\n /** Activate an entry: click, `Enter` or `Space`. Also marks it read when `onMarkRead` is set. */\n onSelect?: (item: NotificationItem) => void;\n /** Mark one entry as read. Enables the per-item read control. */\n onMarkRead?: (id: string) => void;\n /** Mark every entry as read. Enables the header action. */\n onMarkAllRead?: () => void;\n /** Remove one entry. Enables the per-item dismiss control. */\n onDismiss?: (id: string) => void;\n /** Icon rendered at the leading edge of each row — an `<Icon name={…} />`, say. */\n renderIcon?: (item: NotificationItem) => ReactNode;\n /** Locale for the relative timestamps. Default `\"pt-BR\"`. */\n locale?: RelativeTimeLocale;\n /** Shown when `items` is empty. */\n emptyState?: ReactNode;\n /** Reference point for the relative timestamps. Default: now, at render time. */\n now?: number;\n}\n\n/**\n * Inbox panel for notifications the app has received.\n *\n * Deliberately presentational and controlled: it takes a list and emits intent.\n * Where the list comes from — an API, a service-worker push, `localStorage` — and\n * where read state is written are app decisions, and a component that assumed any\n * of them would be wrong for most apps. `useNotificationInbox` is the companion\n * that holds the state, including the service-worker bridge.\n *\n * It is also just the panel, not a popover: apps mount it inside their own\n * `Popover`, `Drawer` or route. That keeps one component from owning both the\n * inbox and a positioning strategy.\n *\n * @example\n * const inbox = useNotificationInbox();\n *\n * <Popover trigger={<Button>Notificações ({inbox.unreadCount})</Button>}>\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * onSelect={(item) => item.url && navigate(item.url)}\n * />\n * </Popover>\n */\nexport function NotificationCenter({\n items,\n title = \"Notificações\",\n onSelect,\n onMarkRead,\n onMarkAllRead,\n onDismiss,\n renderIcon,\n locale = \"pt-BR\",\n emptyState,\n now,\n className,\n ...rest\n}: NotificationCenterProps) {\n const unreadCount = items.filter((item) => !item.read).length;\n const reference = now ?? new Date().getTime();\n\n /**\n * Activate an entry.\n *\n * Reading a notification is implied by opening it, so activation marks it read\n * as well — otherwise every app would have to remember to call both, and the\n * unread badge would keep counting something the user already saw.\n */\n const activate = (item: NotificationItem): void => {\n if (!item.read) onMarkRead?.(item.id);\n onSelect?.(item);\n };\n\n const interactive = Boolean(onSelect || onMarkRead);\n\n return (\n <div className={cn(styles.panel, className)} {...rest}>\n {(title || (onMarkAllRead && unreadCount > 0)) && (\n <header className={styles.header}>\n {title ? (\n <h2 className={styles.title}>\n {title}\n {unreadCount > 0 && (\n <Badge variant=\"primary\" aria-label={`${unreadCount} não lidas`}>\n {unreadCount}\n </Badge>\n )}\n </h2>\n ) : (\n <span />\n )}\n {onMarkAllRead && unreadCount > 0 && (\n <button\n type=\"button\"\n className={styles.headerAction}\n onClick={onMarkAllRead}\n >\n Marcar todas como lidas\n </button>\n )}\n </header>\n )}\n\n {items.length === 0 ? (\n (emptyState ?? <EmptyState title=\"Nenhuma notificação\" />)\n ) : (\n <ul className={styles.list}>\n {items.map((item) => (\n <li\n key={item.id}\n className={cn(styles.item, !item.read && styles.unread)}\n aria-current={!item.read ? \"true\" : undefined}\n >\n {renderIcon && <span className={styles.icon}>{renderIcon(item)}</span>}\n\n <div className={styles.body}>\n {interactive ? (\n <button\n type=\"button\"\n className={styles.trigger}\n onClick={() => activate(item)}\n >\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </button>\n ) : (\n <>\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </>\n )}\n <time\n className={styles.time}\n dateTime={new Date(item.receivedAt).toISOString()}\n >\n {relativeTime(item.receivedAt, { locale, now: reference })}\n </time>\n </div>\n\n {onDismiss && (\n <button\n type=\"button\"\n className={styles.dismiss}\n onClick={() => onDismiss(item.id)}\n aria-label={`Descartar: ${item.title}`}\n >\n ×\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationCenter.js","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an inbox has four actions the app\n * must implement (onSelect, onMarkRead, onMarkAllRead, onDismiss) over one list,\n * plus the presentation it needs to render a readable feed: renderIcon, locale, now\n * for relative timestamps, emptyState and title.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { relativeTime, type RelativeTimeLocale } from \"@/utils/relative-time\";\n\nimport { Badge } from \"../Badge\";\nimport { EmptyState } from \"../EmptyState\";\nimport type { NotificationItem } from \"./use-notification-inbox\";\nimport styles from \"./NotificationCenter.module.css\";\n\n/**\n * DOM attributes this component redefines.\n *\n * `title` becomes a rendered heading node instead of a tooltip string, and\n * `onSelect` becomes \"an entry was activated\" instead of the DOM text-selection\n * event. Both would be type errors if merged, and silently confusing if allowed\n * through.\n */\ntype OverriddenDomProps = \"children\" | \"title\" | \"onSelect\";\n\nexport interface NotificationCenterProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /** Entries to show. Rendered in the order given — `useNotificationInbox` sorts newest first. */\n items: readonly NotificationItem[];\n /**\n * Panel heading. Pass `null` to render the list with no header.\n *\n * Widened from the DOM `title` attribute (a `string`) on purpose: this is a\n * rendered heading, not a tooltip, so it takes a node.\n */\n title?: ReactNode;\n /** Activate an entry: click, `Enter` or `Space`. Also marks it read when `onMarkRead` is set. */\n onSelect?: (item: NotificationItem) => void;\n /** Mark one entry as read. Enables the per-item read control. */\n onMarkRead?: (id: string) => void;\n /** Mark every entry as read. Enables the header action. */\n onMarkAllRead?: () => void;\n /** Remove one entry. Enables the per-item dismiss control. */\n onDismiss?: (id: string) => void;\n /** Icon rendered at the leading edge of each row — an `<Icon name={…} />`, say. */\n renderIcon?: (item: NotificationItem) => ReactNode;\n /** Locale for the relative timestamps. Default `\"pt-BR\"`. */\n locale?: RelativeTimeLocale;\n /** Shown when `items` is empty. */\n emptyState?: ReactNode;\n /** Reference point for the relative timestamps. Default: now, at render time. */\n now?: number;\n}\n\n/**\n * Inbox panel for notifications the app has received.\n *\n * Deliberately presentational and controlled: it takes a list and emits intent.\n * Where the list comes from — an API, a service-worker push, `localStorage` — and\n * where read state is written are app decisions, and a component that assumed any\n * of them would be wrong for most apps. `useNotificationInbox` is the companion\n * that holds the state, including the service-worker bridge.\n *\n * It is also just the panel, not a popover: apps mount it inside their own\n * `Popover`, `Drawer` or route. That keeps one component from owning both the\n * inbox and a positioning strategy.\n *\n * @example\n * const inbox = useNotificationInbox();\n *\n * <Popover trigger={<Button>Notificações ({inbox.unreadCount})</Button>}>\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * onSelect={(item) => item.url && navigate(item.url)}\n * />\n * </Popover>\n */\nexport function NotificationCenter({\n items,\n title = \"Notificações\",\n onSelect,\n onMarkRead,\n onMarkAllRead,\n onDismiss,\n renderIcon,\n locale = \"pt-BR\",\n emptyState,\n now,\n className,\n ...rest\n}: NotificationCenterProps) {\n const unreadCount = items.filter((item) => !item.read).length;\n const reference = now ?? new Date().getTime();\n\n /**\n * Activate an entry.\n *\n * Reading a notification is implied by opening it, so activation marks it read\n * as well — otherwise every app would have to remember to call both, and the\n * unread badge would keep counting something the user already saw.\n */\n const activate = (item: NotificationItem): void => {\n if (!item.read) onMarkRead?.(item.id);\n onSelect?.(item);\n };\n\n const interactive = Boolean(onSelect || onMarkRead);\n\n return (\n <div className={cn(styles.panel, className)} {...rest}>\n {(title || (onMarkAllRead && unreadCount > 0)) && (\n <header className={styles.header}>\n {title ? (\n <h2 className={styles.title}>\n {title}\n {unreadCount > 0 && (\n <Badge variant=\"primary\" aria-label={`${unreadCount} não lidas`}>\n {unreadCount}\n </Badge>\n )}\n </h2>\n ) : (\n <span />\n )}\n {onMarkAllRead && unreadCount > 0 && (\n <button\n type=\"button\"\n className={styles.headerAction}\n onClick={onMarkAllRead}\n >\n Marcar todas como lidas\n </button>\n )}\n </header>\n )}\n\n {items.length === 0 ? (\n (emptyState ?? <EmptyState title=\"Nenhuma notificação\" />)\n ) : (\n <ul className={styles.list}>\n {items.map((item) => (\n <li\n key={item.id}\n className={cn(styles.item, !item.read && styles.unread)}\n aria-current={!item.read ? \"true\" : undefined}\n >\n {renderIcon && <span className={styles.icon}>{renderIcon(item)}</span>}\n\n <div className={styles.body}>\n {interactive ? (\n <button\n type=\"button\"\n className={styles.trigger}\n onClick={() => activate(item)}\n >\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </button>\n ) : (\n <>\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </>\n )}\n <time\n className={styles.time}\n dateTime={new Date(item.receivedAt).toISOString()}\n >\n {relativeTime(item.receivedAt, { locale, now: reference })}\n </time>\n </div>\n\n {onDismiss && (\n <button\n type=\"button\"\n className={styles.dismiss}\n onClick={() => onDismiss(item.id)}\n aria-label={`Descartar: ${item.title}`}\n >\n ×\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAmFA,SAAgB,EAAmB,EAC/B,UACA,WAAQ,gBACR,aACA,eACA,kBACA,cACA,eACA,YAAS,SACT,eACA,QACA,cACA,GAAG,KACqB;CACxB,IAAM,IAAc,EAAM,QAAQ,MAAS,CAAC,EAAK,IAAI,CAAC,CAAC,QACjD,IAAY,sBAAO,IAAI,KAAK,EAAA,CAAE,QAAQ,GAStC,KAAY,MAAiC;EAE/C,AADK,EAAK,QAAM,IAAa,EAAK,EAAE,GACpC,IAAW,CAAI;CACnB,GAEM,IAAc,GAAQ,KAAY;CAExC,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,OAAO,CAAS;EAAG,GAAI;YAAjD,EACM,KAAU,KAAiB,IAAc,MACvC,kBAAC,UAAD;GAAQ,WAAW,EAAO;aAA1B,CACK,IACG,kBAAC,MAAD;IAAI,WAAW,EAAO;cAAtB,CACK,GACA,IAAc,KACX,kBAAC,GAAD;KAAO,SAAQ;KAAU,cAAY,GAAG,EAAY;eAC/C;IACE,CAAA,CAEX;QAEJ,kBAAC,QAAD,CAAO,CAAA,GAEV,KAAiB,IAAc,KAC5B,kBAAC,UAAD;IACI,MAAK;IACL,WAAW,EAAO;IAClB,SAAS;cACZ;GAEO,CAAA,CAER;MAGX,EAAM,WAAW,IACb,KAAc,kBAAC,GAAD,EAAY,OAAM,sBAAuB,CAAA,IAExD,kBAAC,MAAD;GAAI,WAAW,EAAO;aACjB,EAAM,KAAK,MACR,kBAAC,MAAD;IAEI,WAAW,EAAG,EAAO,MAAM,CAAC,EAAK,QAAQ,EAAO,MAAM;IACtD,gBAAe,EAAK,OAAgB,KAAA,IAAT;cAH/B;KAKK,KAAc,kBAAC,QAAD;MAAM,WAAW,EAAO;gBAAO,EAAW,CAAI;KAAQ,CAAA;KAErE,kBAAC,OAAD;MAAK,WAAW,EAAO;gBAAvB,CACK,IACG,kBAAC,UAAD;OACI,MAAK;OACL,WAAW,EAAO;OAClB,eAAe,EAAS,CAAI;iBAHhC,CAKI,kBAAC,QAAD;QAAM,WAAW,EAAO;kBAAY,EAAK;OAAY,CAAA,GACpD,EAAK,QACF,kBAAC,QAAD;QAAM,WAAW,EAAO;kBAAW,EAAK;OAAW,CAAA,CAEnD;WAER,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,QAAD;OAAM,WAAW,EAAO;iBAAY,EAAK;MAAY,CAAA,GACpD,EAAK,QACF,kBAAC,QAAD;OAAM,WAAW,EAAO;iBAAW,EAAK;MAAW,CAAA,CAEzD,EAAA,CAAA,GAEN,kBAAC,QAAD;OACI,WAAW,EAAO;OAClB,UAAU,IAAI,KAAK,EAAK,UAAU,CAAC,CAAC,YAAY;iBAE/C,EAAa,EAAK,YAAY;QAAE;QAAQ,KAAK;OAAU,CAAC;MACvD,CAAA,CACL;;KAEJ,KACG,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,eAAe,EAAU,EAAK,EAAE;MAChC,cAAY,cAAc,EAAK;gBAClC;KAEO,CAAA;IAEZ;MA5CK,EAAK,EA4CV,CACP;EACD,CAAA,CAEP;;AAEb"}
1
+ {"version":3,"file":"NotificationCenter.js","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an inbox has four actions the app\n * must implement (onSelect, onMarkRead, onMarkAllRead, onDismiss) over one list,\n * plus the presentation it needs to render a readable feed: renderIcon, locale, now\n * for relative timestamps, emptyState and title.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { relativeTime, type RelativeTimeLocale } from \"@/utils/relative-time\";\n\nimport { Badge } from \"../Badge\";\nimport { EmptyState } from \"../EmptyState\";\nimport type { NotificationItem } from \"./use-notification-inbox\";\nimport styles from \"./NotificationCenter.module.css\";\n\n/**\n * DOM attributes this component redefines.\n *\n * `title` becomes a rendered heading node instead of a tooltip string, and\n * `onSelect` becomes \"an entry was activated\" instead of the DOM text-selection\n * event. Both would be type errors if merged, and silently confusing if allowed\n * through.\n */\ntype OverriddenDomProps = \"children\" | \"title\" | \"onSelect\";\n\nexport interface NotificationCenterProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /** Entries to show. Rendered in the order given — `useNotificationInbox` sorts newest first. */\n items: readonly NotificationItem[];\n /**\n * Panel heading. Pass `null` to render the list with no header.\n *\n * Widened from the DOM `title` attribute (a `string`) on purpose: this is a\n * rendered heading, not a tooltip, so it takes a node.\n */\n title?: ReactNode;\n /** Activate an entry: click, `Enter` or `Space`. Also marks it read when `onMarkRead` is set. */\n onSelect?: (item: NotificationItem) => void;\n /** Mark one entry as read. Enables the per-item read control. */\n onMarkRead?: (id: string) => void;\n /** Mark every entry as read. Enables the header action. */\n onMarkAllRead?: () => void;\n /** Remove one entry. Enables the per-item dismiss control. */\n onDismiss?: (id: string) => void;\n /** Icon rendered at the leading edge of each row — an `<Icon name={…} />`, say. */\n renderIcon?: (item: NotificationItem) => ReactNode;\n /** Locale for the relative timestamps. Default `\"pt-BR\"`. */\n locale?: RelativeTimeLocale;\n /** Shown when `items` is empty. */\n emptyState?: ReactNode;\n /** Reference point for the relative timestamps. Default: now, at render time. */\n now?: number;\n}\n\n/**\n * Inbox panel for notifications the app has received.\n *\n * Deliberately presentational and controlled: it takes a list and emits intent.\n * Where the list comes from — an API, a service-worker push, `localStorage` — and\n * where read state is written are app decisions, and a component that assumed any\n * of them would be wrong for most apps. `useNotificationInbox` is the companion\n * that holds the state, including the service-worker bridge.\n *\n * It is also just the panel, not a popover: apps mount it inside their own\n * `Popover`, `Drawer` or route. That keeps one component from owning both the\n * inbox and a positioning strategy.\n *\n * @example\n * const inbox = useNotificationInbox();\n *\n * <Popover trigger={<Button>Notificações ({inbox.unreadCount})</Button>}>\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * onSelect={(item) => item.url && navigate(item.url)}\n * />\n * </Popover>\n */\nexport function NotificationCenter({\n items,\n title = \"Notificações\",\n onSelect,\n onMarkRead,\n onMarkAllRead,\n onDismiss,\n renderIcon,\n locale = \"pt-BR\",\n emptyState,\n now,\n className,\n ...rest\n}: NotificationCenterProps) {\n const unreadCount = items.filter((item) => !item.read).length;\n const reference = now ?? new Date().getTime();\n\n /**\n * Activate an entry.\n *\n * Reading a notification is implied by opening it, so activation marks it read\n * as well — otherwise every app would have to remember to call both, and the\n * unread badge would keep counting something the user already saw.\n */\n const activate = (item: NotificationItem): void => {\n if (!item.read) onMarkRead?.(item.id);\n onSelect?.(item);\n };\n\n const interactive = Boolean(onSelect || onMarkRead);\n\n return (\n <div className={cn(styles.panel, className)} {...rest}>\n {(title || (onMarkAllRead && unreadCount > 0)) && (\n <header className={styles.header}>\n {title ? (\n <h2 className={styles.title}>\n {title}\n {unreadCount > 0 && (\n <Badge variant=\"primary\" aria-label={`${unreadCount} não lidas`}>\n {unreadCount}\n </Badge>\n )}\n </h2>\n ) : (\n <span />\n )}\n {onMarkAllRead && unreadCount > 0 && (\n <button\n type=\"button\"\n className={styles.headerAction}\n onClick={onMarkAllRead}\n >\n Marcar todas como lidas\n </button>\n )}\n </header>\n )}\n\n {items.length === 0 ? (\n (emptyState ?? <EmptyState title=\"Nenhuma notificação\" />)\n ) : (\n <ul className={styles.list}>\n {items.map((item) => (\n <li\n key={item.id}\n className={cn(styles.item, !item.read && styles.unread)}\n aria-current={!item.read ? \"true\" : undefined}\n >\n {renderIcon && <span className={styles.icon}>{renderIcon(item)}</span>}\n\n <div className={styles.body}>\n {interactive ? (\n <button\n type=\"button\"\n className={styles.trigger}\n onClick={() => activate(item)}\n >\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </button>\n ) : (\n <>\n <span className={styles.itemTitle}>{item.title}</span>\n {item.body && (\n <span className={styles.itemBody}>{item.body}</span>\n )}\n </>\n )}\n <time\n className={styles.time}\n dateTime={new Date(item.receivedAt).toISOString()}\n >\n {relativeTime(item.receivedAt, { locale, now: reference })}\n </time>\n </div>\n\n {onDismiss && (\n <button\n type=\"button\"\n className={styles.dismiss}\n onClick={() => onDismiss(item.id)}\n aria-label={`Descartar: ${item.title}`}\n >\n ×\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAmFA,SAAgB,EAAmB,EAC/B,UACA,WAAQ,gBACR,aACA,eACA,kBACA,cACA,eACA,YAAS,SACT,eACA,QACA,cACA,GAAG,KACqB;CACxB,IAAM,IAAc,EAAM,QAAQ,MAAS,CAAC,EAAK,IAAI,CAAC,CAAC,QACjD,IAAY,sBAAO,IAAI,KAAK,EAAA,CAAE,QAAQ,GAStC,KAAY,MAAiC;EAE/C,AADK,EAAK,QAAM,IAAa,EAAK,EAAE,GACpC,IAAW,CAAI;CACnB,GAEM,IAAc,GAAQ,KAAY;CAExC,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,OAAO,CAAS;EAAG,GAAI;EAAjD,UAAA,EACM,KAAU,KAAiB,IAAc,MACvC,kBAAC,UAAD;GAAQ,WAAW,EAAO;GAA1B,UAAA,CACK,IACG,kBAAC,MAAD;IAAI,WAAW,EAAO;IAAtB,UAAA,CACK,GACA,IAAc,KACX,kBAAC,GAAD;KAAO,SAAQ;KAAU,cAAY,GAAG,EAAY;KAC/C,UAAA;IACE,CAAA,CAEX;GAEJ,CAAA,IAAA,kBAAC,QAAD,CAAO,CAAA,GAEV,KAAiB,IAAc,KAC5B,kBAAC,UAAD;IACI,MAAK;IACL,WAAW,EAAO;IAClB,SAAS;IACZ,UAAA;GAEO,CAAA,CAER;EAGX,CAAA,GAAA,EAAM,WAAW,IACb,KAAc,kBAAC,GAAD,EAAY,OAAM,sBAAuB,CAAA,IAExD,kBAAC,MAAD;GAAI,WAAW,EAAO;GACjB,UAAA,EAAM,KAAK,MACR,kBAAC,MAAD;IAEI,WAAW,EAAG,EAAO,MAAM,CAAC,EAAK,QAAQ,EAAO,MAAM;IACtD,gBAAe,EAAK,OAAgB,KAAA,IAAT;IAH/B,UAAA;KAKK,KAAc,kBAAC,QAAD;MAAM,WAAW,EAAO;MAAO,UAAA,EAAW,CAAI;KAAQ,CAAA;KAErE,kBAAC,OAAD;MAAK,WAAW,EAAO;MAAvB,UAAA,CACK,IACG,kBAAC,UAAD;OACI,MAAK;OACL,WAAW,EAAO;OAClB,eAAe,EAAS,CAAI;OAHhC,UAAA,CAKI,kBAAC,QAAD;QAAM,WAAW,EAAO;QAAY,UAAA,EAAK;OAAY,CAAA,GACpD,EAAK,QACF,kBAAC,QAAD;QAAM,WAAW,EAAO;QAAW,UAAA,EAAK;OAAW,CAAA,CAEnD;MAER,CAAA,IAAA,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,QAAD;OAAM,WAAW,EAAO;OAAY,UAAA,EAAK;MAAY,CAAA,GACpD,EAAK,QACF,kBAAC,QAAD;OAAM,WAAW,EAAO;OAAW,UAAA,EAAK;MAAW,CAAA,CAEzD,EAAA,CAAA,GAEN,kBAAC,QAAD;OACI,WAAW,EAAO;OAClB,UAAU,IAAI,KAAK,EAAK,UAAU,CAAC,CAAC,YAAY;OAE/C,UAAA,EAAa,EAAK,YAAY;QAAE;QAAQ,KAAK;OAAU,CAAC;MACvD,CAAA,CACL;;KAEJ,KACG,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,eAAe,EAAU,EAAK,EAAE;MAChC,cAAY,cAAc,EAAK;MAClC,UAAA;KAEO,CAAA;IAEZ;GA5CK,GAAA,EAAK,EA4CV,CACP;EACD,CAAA,CAEP;;AAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-notification-inbox.cjs","names":[],"sources":["../../../src/components/NotificationCenter/use-notification-inbox.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\n\n/** One entry in the inbox. */\nexport interface NotificationItem {\n /** Stable id. Re-adding an existing id updates that entry instead of duplicating it. */\n id: string;\n title: string;\n /** Body text. Optional — a title-only notification is a legitimate shape. */\n body?: string;\n /** Epoch milliseconds. Used for ordering and for the relative timestamp. */\n receivedAt: number;\n read?: boolean;\n /** Where activating the notification should take the user. */\n url?: string;\n /** Free-form payload the app passes through, e.g. the entity it refers to. */\n data?: Record<string, unknown>;\n}\n\nexport interface UseNotificationInboxOptions {\n /** Entries the inbox starts with — typically what the API returned. */\n initialItems?: readonly NotificationItem[];\n /**\n * Listen for `message` events from the service worker and add whatever\n * arrives. Default `true`.\n *\n * This is the missing half of web push: a service worker runs outside the\n * page and cannot touch React state, so a push that arrives while the app is\n * open shows an OS notification and then vanishes as far as the UI is\n * concerned. Have the worker `postMessage` the payload and it lands here.\n */\n listenToServiceWorker?: boolean;\n /**\n * Message `type` to accept from the worker. Default `\"tempest:notification\"`.\n *\n * Filtering by type matters because the SW message channel is shared — a\n * sync-progress ping or a cache-updated notice would otherwise show up in the\n * user's inbox.\n */\n messageType?: string;\n /**\n * Cap on stored entries; the oldest are dropped past it. Default `100`.\n *\n * An inbox fed by push grows without bound otherwise, and it lives in memory.\n */\n limit?: number;\n /** Called whenever the list changes — the hook for persisting it. */\n onChange?: (items: readonly NotificationItem[]) => void;\n}\n\nexport interface UseNotificationInboxResult {\n items: NotificationItem[];\n unreadCount: number;\n /** Add an entry, or update the existing one with the same id. */\n add: (item: NotificationItem) => void;\n markRead: (id: string) => void;\n markUnread: (id: string) => void;\n markAllRead: () => void;\n remove: (id: string) => void;\n clear: () => void;\n}\n\n/** Coerce an unknown service-worker payload into an inbox entry. */\nfunction toItem(payload: Record<string, unknown>, index: number): NotificationItem | null {\n const title = typeof payload.title === \"string\" ? payload.title : null;\n if (!title) return null;\n return {\n id: typeof payload.id === \"string\" ? payload.id : `sw-${payload.tag ?? index}-${title}`,\n title,\n body: typeof payload.body === \"string\" ? payload.body : undefined,\n receivedAt:\n typeof payload.receivedAt === \"number\" ? payload.receivedAt : new Date().getTime(),\n url: typeof payload.url === \"string\" ? payload.url : undefined,\n data: (payload.data as Record<string, unknown> | undefined) ?? undefined,\n read: false,\n };\n}\n\n/**\n * Client-side inbox state for received notifications.\n *\n * Newest first, deduplicated by `id`, capped at `limit`. Pairs with\n * `NotificationCenter` for the UI and with the `push` module for the source:\n * point a service worker's `postMessage` at it and a push that arrives while the\n * app is open shows up in the inbox instead of only as an OS notification.\n *\n * Persistence is deliberately left out — where an inbox belongs (server, Dexie,\n * `localStorage`) is an app decision. Use `onChange` to write it wherever it goes,\n * and `initialItems` to read it back.\n *\n * @example\n * const inbox = useNotificationInbox({\n * initialItems: await api.notifications.list(),\n * onChange: (items) => storage.set(\"inbox\", items),\n * });\n *\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * />\n */\nexport function useNotificationInbox(\n options: UseNotificationInboxOptions = {},\n): UseNotificationInboxResult {\n const {\n initialItems = [],\n listenToServiceWorker = true,\n messageType = \"tempest:notification\",\n limit = 100,\n onChange,\n } = options;\n\n const [items, setItems] = useState<NotificationItem[]>(() =>\n [...initialItems].sort((a, b) => b.receivedAt - a.receivedAt).slice(0, limit),\n );\n\n const update = useCallback(\n (next: (current: NotificationItem[]) => NotificationItem[]) => {\n setItems((current) => {\n const result = next(current);\n onChange?.(result);\n return result;\n });\n },\n [onChange],\n );\n\n const add = useCallback(\n (item: NotificationItem) => {\n update((current) => {\n const without = current.filter((entry) => entry.id !== item.id);\n return [item, ...without]\n .sort((a, b) => b.receivedAt - a.receivedAt)\n .slice(0, limit);\n });\n },\n [update, limit],\n );\n\n const setRead = useCallback(\n (id: string, read: boolean) => {\n update((current) =>\n current.map((entry) => (entry.id === id ? { ...entry, read } : entry)),\n );\n },\n [update],\n );\n\n const markRead = useCallback((id: string) => setRead(id, true), [setRead]);\n const markUnread = useCallback((id: string) => setRead(id, false), [setRead]);\n\n const markAllRead = useCallback(() => {\n update((current) => current.map((entry) => ({ ...entry, read: true })));\n }, [update]);\n\n const remove = useCallback(\n (id: string) => {\n update((current) => current.filter((entry) => entry.id !== id));\n },\n [update],\n );\n\n const clear = useCallback(() => update(() => []), [update]);\n\n useEffect(() => {\n if (!listenToServiceWorker) return;\n if (typeof navigator === \"undefined\" || !navigator.serviceWorker) return;\n\n /**\n * Captured, not re-read in the cleanup.\n *\n * Looking `navigator.serviceWorker` up again at teardown throws if it is\n * gone by then, and the listener would leak either way — the container has\n * to be the same object the listener was added to.\n */\n const container = navigator.serviceWorker;\n\n const handler = (event: MessageEvent) => {\n const payload = event.data as Record<string, unknown> | null;\n if (!payload || payload.type !== messageType) return;\n const raw = (payload.notification ?? payload.payload ?? payload) as Record<\n string,\n unknown\n >;\n const item = toItem(raw, 0);\n if (item) add(item);\n };\n\n container.addEventListener(\"message\", handler);\n return () => container.removeEventListener(\"message\", handler);\n }, [listenToServiceWorker, messageType, add]);\n\n const unreadCount = useMemo(() => items.filter((item) => !item.read).length, [items]);\n\n return { items, unreadCount, add, markRead, markUnread, markAllRead, remove, clear };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-notification-inbox.cjs","names":[],"sources":["../../../src/components/NotificationCenter/use-notification-inbox.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\n\n/** One entry in the inbox. */\nexport interface NotificationItem {\n /** Stable id. Re-adding an existing id updates that entry instead of duplicating it. */\n id: string;\n title: string;\n /** Body text. Optional — a title-only notification is a legitimate shape. */\n body?: string;\n /** Epoch milliseconds. Used for ordering and for the relative timestamp. */\n receivedAt: number;\n read?: boolean;\n /** Where activating the notification should take the user. */\n url?: string;\n /** Free-form payload the app passes through, e.g. the entity it refers to. */\n data?: Record<string, unknown>;\n}\n\nexport interface UseNotificationInboxOptions {\n /** Entries the inbox starts with — typically what the API returned. */\n initialItems?: readonly NotificationItem[];\n /**\n * Listen for `message` events from the service worker and add whatever\n * arrives. Default `true`.\n *\n * This is the missing half of web push: a service worker runs outside the\n * page and cannot touch React state, so a push that arrives while the app is\n * open shows an OS notification and then vanishes as far as the UI is\n * concerned. Have the worker `postMessage` the payload and it lands here.\n */\n listenToServiceWorker?: boolean;\n /**\n * Message `type` to accept from the worker. Default `\"tempest:notification\"`.\n *\n * Filtering by type matters because the SW message channel is shared — a\n * sync-progress ping or a cache-updated notice would otherwise show up in the\n * user's inbox.\n */\n messageType?: string;\n /**\n * Cap on stored entries; the oldest are dropped past it. Default `100`.\n *\n * An inbox fed by push grows without bound otherwise, and it lives in memory.\n */\n limit?: number;\n /** Called whenever the list changes — the hook for persisting it. */\n onChange?: (items: readonly NotificationItem[]) => void;\n}\n\nexport interface UseNotificationInboxResult {\n items: NotificationItem[];\n unreadCount: number;\n /** Add an entry, or update the existing one with the same id. */\n add: (item: NotificationItem) => void;\n markRead: (id: string) => void;\n markUnread: (id: string) => void;\n markAllRead: () => void;\n remove: (id: string) => void;\n clear: () => void;\n}\n\n/** Coerce an unknown service-worker payload into an inbox entry. */\nfunction toItem(payload: Record<string, unknown>, index: number): NotificationItem | null {\n const title = typeof payload.title === \"string\" ? payload.title : null;\n if (!title) return null;\n return {\n id: typeof payload.id === \"string\" ? payload.id : `sw-${payload.tag ?? index}-${title}`,\n title,\n body: typeof payload.body === \"string\" ? payload.body : undefined,\n receivedAt:\n typeof payload.receivedAt === \"number\" ? payload.receivedAt : new Date().getTime(),\n url: typeof payload.url === \"string\" ? payload.url : undefined,\n data: (payload.data as Record<string, unknown> | undefined) ?? undefined,\n read: false,\n };\n}\n\n/**\n * Client-side inbox state for received notifications.\n *\n * Newest first, deduplicated by `id`, capped at `limit`. Pairs with\n * `NotificationCenter` for the UI and with the `push` module for the source:\n * point a service worker's `postMessage` at it and a push that arrives while the\n * app is open shows up in the inbox instead of only as an OS notification.\n *\n * Persistence is deliberately left out — where an inbox belongs (server, Dexie,\n * `localStorage`) is an app decision. Use `onChange` to write it wherever it goes,\n * and `initialItems` to read it back.\n *\n * @example\n * const inbox = useNotificationInbox({\n * initialItems: await api.notifications.list(),\n * onChange: (items) => storage.set(\"inbox\", items),\n * });\n *\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * />\n */\nexport function useNotificationInbox(\n options: UseNotificationInboxOptions = {},\n): UseNotificationInboxResult {\n const {\n initialItems = [],\n listenToServiceWorker = true,\n messageType = \"tempest:notification\",\n limit = 100,\n onChange,\n } = options;\n\n const [items, setItems] = useState<NotificationItem[]>(() =>\n [...initialItems].sort((a, b) => b.receivedAt - a.receivedAt).slice(0, limit),\n );\n\n const update = useCallback(\n (next: (current: NotificationItem[]) => NotificationItem[]) => {\n setItems((current) => {\n const result = next(current);\n onChange?.(result);\n return result;\n });\n },\n [onChange],\n );\n\n const add = useCallback(\n (item: NotificationItem) => {\n update((current) => {\n const without = current.filter((entry) => entry.id !== item.id);\n return [item, ...without]\n .sort((a, b) => b.receivedAt - a.receivedAt)\n .slice(0, limit);\n });\n },\n [update, limit],\n );\n\n const setRead = useCallback(\n (id: string, read: boolean) => {\n update((current) =>\n current.map((entry) => (entry.id === id ? { ...entry, read } : entry)),\n );\n },\n [update],\n );\n\n const markRead = useCallback((id: string) => setRead(id, true), [setRead]);\n const markUnread = useCallback((id: string) => setRead(id, false), [setRead]);\n\n const markAllRead = useCallback(() => {\n update((current) => current.map((entry) => ({ ...entry, read: true })));\n }, [update]);\n\n const remove = useCallback(\n (id: string) => {\n update((current) => current.filter((entry) => entry.id !== id));\n },\n [update],\n );\n\n const clear = useCallback(() => update(() => []), [update]);\n\n useEffect(() => {\n if (!listenToServiceWorker) return;\n if (typeof navigator === \"undefined\" || !navigator.serviceWorker) return;\n\n /**\n * Captured, not re-read in the cleanup.\n *\n * Looking `navigator.serviceWorker` up again at teardown throws if it is\n * gone by then, and the listener would leak either way — the container has\n * to be the same object the listener was added to.\n */\n const container = navigator.serviceWorker;\n\n const handler = (event: MessageEvent) => {\n const payload = event.data as Record<string, unknown> | null;\n if (!payload || payload.type !== messageType) return;\n const raw = (payload.notification ?? payload.payload ?? payload) as Record<\n string,\n unknown\n >;\n const item = toItem(raw, 0);\n if (item) add(item);\n };\n\n container.addEventListener(\"message\", handler);\n return () => container.removeEventListener(\"message\", handler);\n }, [listenToServiceWorker, messageType, add]);\n\n const unreadCount = useMemo(() => items.filter((item) => !item.read).length, [items]);\n\n return { items, unreadCount, add, markRead, markUnread, markAllRead, remove, clear };\n}\n"],"mappings":"uBA8DA,SAAS,EAAO,EAAkC,EAAwC,CACtF,IAAM,EAAQ,OAAO,EAAQ,OAAU,SAAW,EAAQ,MAAQ,KAElE,OADK,EACE,CACH,GAAI,OAAO,EAAQ,IAAO,SAAW,EAAQ,GAAK,MAAM,EAAQ,KAAO,EAAM,GAAG,IAChF,QACA,KAAM,OAAO,EAAQ,MAAS,SAAW,EAAQ,KAAO,IAAA,GACxD,WACI,OAAO,EAAQ,YAAe,SAAW,EAAQ,WAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,EACrF,IAAK,OAAO,EAAQ,KAAQ,SAAW,EAAQ,IAAM,IAAA,GACrD,KAAO,EAAQ,MAAgD,IAAA,GAC/D,KAAM,EACV,EAVmB,IAWvB,CA2BA,SAAgB,EACZ,EAAuC,CAAC,EACd,CAC1B,GAAM,CACF,eAAe,CAAC,EAChB,wBAAwB,GACxB,cAAc,uBACd,QAAQ,IACR,YACA,EAEE,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,KACtB,CAAC,GAAG,CAAY,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,WAAa,EAAE,UAAU,CAAC,CAAC,MAAM,EAAG,CAAK,CAChF,EAEM,GAAA,EAAS,EAAA,YAAA,CACV,GAA8D,CAC3D,EAAU,GAAY,CAClB,IAAM,EAAS,EAAK,CAAO,EAE3B,OADA,IAAW,CAAM,EACV,CACX,CAAC,CACL,EACA,CAAC,CAAQ,CACb,EAEM,GAAA,EAAM,EAAA,YAAA,CACP,GAA2B,CACxB,EAAQ,GAEG,CAAC,EAAM,GADE,EAAQ,OAAQ,GAAU,EAAM,KAAO,EAAK,EAC3C,CAAO,CAAC,CACpB,MAAM,EAAG,IAAM,EAAE,WAAa,EAAE,UAAU,CAAC,CAC3C,MAAM,EAAG,CAAK,CACtB,CACL,EACA,CAAC,EAAQ,CAAK,CAClB,EAEM,GAAA,EAAU,EAAA,YAAA,EACX,EAAY,IAAkB,CAC3B,EAAQ,GACJ,EAAQ,IAAK,GAAW,EAAM,KAAO,EAAK,CAAE,GAAG,EAAO,MAAK,EAAI,CAAM,CACzE,CACJ,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAW,EAAA,YAAA,CAAa,GAAe,EAAQ,EAAI,EAAI,EAAG,CAAC,CAAO,CAAC,EACnE,GAAA,EAAa,EAAA,YAAA,CAAa,GAAe,EAAQ,EAAI,EAAK,EAAG,CAAC,CAAO,CAAC,EAEtE,GAAA,EAAc,EAAA,YAAA,KAAkB,CAClC,EAAQ,GAAY,EAAQ,IAAK,IAAW,CAAE,GAAG,EAAO,KAAM,EAAK,EAAE,CAAC,CAC1E,EAAG,CAAC,CAAM,CAAC,EAEL,GAAA,EAAS,EAAA,YAAA,CACV,GAAe,CACZ,EAAQ,GAAY,EAAQ,OAAQ,GAAU,EAAM,KAAO,CAAE,CAAC,CAClE,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAQ,EAAA,YAAA,KAAkB,MAAa,CAAC,CAAC,EAAG,CAAC,CAAM,CAAC,EAgC1D,OA9BA,EAAA,EAAA,UAAA,KAAgB,CAEZ,GADI,CAAC,GACD,OAAO,UAAc,KAAe,CAAC,UAAU,cAAe,OASlE,IAAM,EAAY,UAAU,cAEtB,EAAW,GAAwB,CACrC,IAAM,EAAU,EAAM,KACtB,GAAI,CAAC,GAAW,EAAQ,OAAS,EAAa,OAK9C,IAAM,EAAO,EAJA,EAAQ,cAAgB,EAAQ,SAAW,EAI/B,CAAC,EACtB,GAAM,EAAI,CAAI,CACtB,EAGA,OADA,EAAU,iBAAiB,UAAW,CAAO,MAChC,EAAU,oBAAoB,UAAW,CAAO,CACjE,EAAG,CAAC,EAAuB,EAAa,CAAG,CAAC,EAIrC,CAAE,QAAO,aAAA,EAFI,EAAA,QAAA,KAAc,EAAM,OAAQ,GAAS,CAAC,EAAK,IAAI,CAAC,CAAC,OAAQ,CAAC,CAAK,CAEnE,EAAa,MAAK,WAAU,aAAY,cAAa,SAAQ,OAAM,CACvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"OfflineIndicator.cjs","names":[],"sources":["../../../src/components/OfflineIndicator/OfflineIndicator.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { CloudOff, Wifi } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useOnline } from \"@/hooks\";\nimport styles from \"./OfflineIndicator.module.css\";\n\nexport type OfflineIndicatorPosition = \"top\" | \"bottom\";\n\nexport interface OfflineIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n /** Where to pin the fixed bar. Default `\"bottom\"`. */\n position?: OfflineIndicatorPosition;\n /** Message shown while offline. */\n offlineLabel?: ReactNode;\n /** Message flashed briefly when connectivity returns. */\n onlineLabel?: ReactNode;\n /**\n * How long (ms) to keep the \"back online\" flash visible. `0` hides it\n * entirely. Default `3000`.\n */\n onlineFlashMs?: number;\n /** Override the offline body entirely (icon + label ignored). */\n children?: ReactNode;\n}\n\n/**\n * Fixed bar that appears while the browser is offline and, by default, flashes\n * a brief confirmation when the connection returns. Backed by {@link useOnline}\n * — no props needed for the common case.\n *\n * Renders nothing while online (after any flash has elapsed), so it is safe to\n * mount unconditionally at the app root.\n *\n * @example\n * <OfflineIndicator position=\"top\" />\n */\nexport function OfflineIndicator({\n position = \"bottom\",\n offlineLabel = \"Você está offline. As alterações serão sincronizadas ao reconectar.\",\n onlineLabel = \"Conexão restabelecida.\",\n onlineFlashMs = 3000,\n className,\n children,\n ...props\n}: OfflineIndicatorProps) {\n const online = useOnline();\n const [flashOnline, setFlashOnline] = useState<boolean>(false);\n const wasOfflineRef = useRef<boolean>(false);\n\n useEffect(() => {\n if (!online) {\n wasOfflineRef.current = true;\n setFlashOnline(false);\n return;\n }\n if (!wasOfflineRef.current || onlineFlashMs <= 0) return;\n wasOfflineRef.current = false;\n setFlashOnline(true);\n const id = window.setTimeout(() => setFlashOnline(false), onlineFlashMs);\n return () => window.clearTimeout(id);\n }, [online, onlineFlashMs]);\n\n if (online && !flashOnline) return null;\n\n const showingOnline = online && flashOnline;\n return (\n <div\n className={cn(\n styles.bar,\n styles[position],\n showingOnline ? styles.online : styles.offline,\n className,\n )}\n role=\"status\"\n aria-live=\"polite\"\n {...props}\n >\n {children ?? (\n <>\n {showingOnline ? (\n <Wifi className={styles.icon} size={16} aria-hidden=\"true\" />\n ) : (\n <CloudOff className={styles.icon} size={16} aria-hidden=\"true\" />\n )}\n <span>{showingOnline ? onlineLabel : offlineLabel}</span>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"yMAoCA,SAAgB,EAAiB,CAC7B,WAAW,SACX,eAAe,sEACf,cAAc,yBACd,gBAAgB,IAChB,YACA,WACA,GAAG,GACmB,CACtB,IAAM,EAAS,EAAA,UAAU,EACnB,CAAC,EAAa,IAAA,EAAA,EAAA,SAAA,CAAoC,EAAK,EACvD,GAAA,EAAA,EAAA,OAAA,CAAgC,EAAK,EAe3C,IAbA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAQ,CACT,EAAc,QAAU,GACxB,EAAe,EAAK,EACpB,MACJ,CACA,GAAI,CAAC,EAAc,SAAW,GAAiB,EAAG,OAClD,EAAc,QAAU,GACxB,EAAe,EAAI,EACnB,IAAM,EAAK,OAAO,eAAiB,EAAe,EAAK,EAAG,CAAa,EACvE,UAAa,OAAO,aAAa,CAAE,CACvC,EAAG,CAAC,EAAQ,CAAa,CAAC,EAEtB,GAAU,CAAC,EAAa,OAAO,KAEnC,IAAM,EAAgB,GAAU,EAChC,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,IACP,EAAA,QAAO,GACP,EAAgB,EAAA,QAAO,OAAS,EAAA,QAAO,QACvC,CACJ,EACA,KAAK,SACL,YAAU,SACV,GAAI,WAEH,IACG,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACK,GACG,EAAA,EAAA,IAAA,CAAC,EAAA,KAAD,CAAM,UAAW,EAAA,QAAO,KAAM,KAAM,GAAI,cAAY,MAAQ,CAAA,GAE5D,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAU,UAAW,EAAA,QAAO,KAAM,KAAM,GAAI,cAAY,MAAQ,CAAA,GAEpE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAgB,EAAc,CAAmB,CAAA,CAC1D,CAAA,CAAA,CAEL,CAAA,CAEb"}
1
+ {"version":3,"file":"OfflineIndicator.cjs","names":[],"sources":["../../../src/components/OfflineIndicator/OfflineIndicator.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { CloudOff, Wifi } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useOnline } from \"@/hooks\";\nimport styles from \"./OfflineIndicator.module.css\";\n\nexport type OfflineIndicatorPosition = \"top\" | \"bottom\";\n\nexport interface OfflineIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n /** Where to pin the fixed bar. Default `\"bottom\"`. */\n position?: OfflineIndicatorPosition;\n /** Message shown while offline. */\n offlineLabel?: ReactNode;\n /** Message flashed briefly when connectivity returns. */\n onlineLabel?: ReactNode;\n /**\n * How long (ms) to keep the \"back online\" flash visible. `0` hides it\n * entirely. Default `3000`.\n */\n onlineFlashMs?: number;\n /** Override the offline body entirely (icon + label ignored). */\n children?: ReactNode;\n}\n\n/**\n * Fixed bar that appears while the browser is offline and, by default, flashes\n * a brief confirmation when the connection returns. Backed by {@link useOnline}\n * — no props needed for the common case.\n *\n * Renders nothing while online (after any flash has elapsed), so it is safe to\n * mount unconditionally at the app root.\n *\n * @example\n * <OfflineIndicator position=\"top\" />\n */\nexport function OfflineIndicator({\n position = \"bottom\",\n offlineLabel = \"Você está offline. As alterações serão sincronizadas ao reconectar.\",\n onlineLabel = \"Conexão restabelecida.\",\n onlineFlashMs = 3000,\n className,\n children,\n ...props\n}: OfflineIndicatorProps) {\n const online = useOnline();\n const [flashOnline, setFlashOnline] = useState<boolean>(false);\n const wasOfflineRef = useRef<boolean>(false);\n\n useEffect(() => {\n if (!online) {\n wasOfflineRef.current = true;\n setFlashOnline(false);\n return;\n }\n if (!wasOfflineRef.current || onlineFlashMs <= 0) return;\n wasOfflineRef.current = false;\n setFlashOnline(true);\n const id = window.setTimeout(() => setFlashOnline(false), onlineFlashMs);\n return () => window.clearTimeout(id);\n }, [online, onlineFlashMs]);\n\n if (online && !flashOnline) return null;\n\n const showingOnline = online && flashOnline;\n return (\n <div\n className={cn(\n styles.bar,\n styles[position],\n showingOnline ? styles.online : styles.offline,\n className,\n )}\n role=\"status\"\n aria-live=\"polite\"\n {...props}\n >\n {children ?? (\n <>\n {showingOnline ? (\n <Wifi className={styles.icon} size={16} aria-hidden=\"true\" />\n ) : (\n <CloudOff className={styles.icon} size={16} aria-hidden=\"true\" />\n )}\n <span>{showingOnline ? onlineLabel : offlineLabel}</span>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"yMAoCA,SAAgB,EAAiB,CAC7B,WAAW,SACX,eAAe,sEACf,cAAc,yBACd,gBAAgB,IAChB,YACA,WACA,GAAG,GACmB,CACtB,IAAM,EAAS,EAAA,UAAU,EACnB,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAkB,EAAK,EACvD,GAAA,EAAgB,EAAA,OAAA,CAAgB,EAAK,EAe3C,IAbA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAQ,CACT,EAAc,QAAU,GACxB,EAAe,EAAK,EACpB,MACJ,CACA,GAAI,CAAC,EAAc,SAAW,GAAiB,EAAG,OAClD,EAAc,QAAU,GACxB,EAAe,EAAI,EACnB,IAAM,EAAK,OAAO,eAAiB,EAAe,EAAK,EAAG,CAAa,EACvE,UAAa,OAAO,aAAa,CAAE,CACvC,EAAG,CAAC,EAAQ,CAAa,CAAC,EAEtB,GAAU,CAAC,EAAa,OAAO,KAEnC,IAAM,EAAgB,GAAU,EAChC,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,IACP,EAAA,QAAO,GACP,EAAgB,EAAA,QAAO,OAAS,EAAA,QAAO,QACvC,CACJ,EACA,KAAK,SACL,YAAU,SACV,GAAI,EAEH,SAAA,IACG,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACK,GACG,EAAA,EAAA,IAAA,CAAC,EAAA,KAAD,CAAM,UAAW,EAAA,QAAO,KAAM,KAAM,GAAI,cAAY,MAAQ,CAAA,GAE5D,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CAAU,UAAW,EAAA,QAAO,KAAM,KAAM,GAAI,cAAY,MAAQ,CAAA,GAEpE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAgB,EAAc,CAAmB,CAAA,CAC1D,CAAA,CAAA,CAEL,CAAA,CAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"OfflineIndicator.js","names":[],"sources":["../../../src/components/OfflineIndicator/OfflineIndicator.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { CloudOff, Wifi } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useOnline } from \"@/hooks\";\nimport styles from \"./OfflineIndicator.module.css\";\n\nexport type OfflineIndicatorPosition = \"top\" | \"bottom\";\n\nexport interface OfflineIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n /** Where to pin the fixed bar. Default `\"bottom\"`. */\n position?: OfflineIndicatorPosition;\n /** Message shown while offline. */\n offlineLabel?: ReactNode;\n /** Message flashed briefly when connectivity returns. */\n onlineLabel?: ReactNode;\n /**\n * How long (ms) to keep the \"back online\" flash visible. `0` hides it\n * entirely. Default `3000`.\n */\n onlineFlashMs?: number;\n /** Override the offline body entirely (icon + label ignored). */\n children?: ReactNode;\n}\n\n/**\n * Fixed bar that appears while the browser is offline and, by default, flashes\n * a brief confirmation when the connection returns. Backed by {@link useOnline}\n * — no props needed for the common case.\n *\n * Renders nothing while online (after any flash has elapsed), so it is safe to\n * mount unconditionally at the app root.\n *\n * @example\n * <OfflineIndicator position=\"top\" />\n */\nexport function OfflineIndicator({\n position = \"bottom\",\n offlineLabel = \"Você está offline. As alterações serão sincronizadas ao reconectar.\",\n onlineLabel = \"Conexão restabelecida.\",\n onlineFlashMs = 3000,\n className,\n children,\n ...props\n}: OfflineIndicatorProps) {\n const online = useOnline();\n const [flashOnline, setFlashOnline] = useState<boolean>(false);\n const wasOfflineRef = useRef<boolean>(false);\n\n useEffect(() => {\n if (!online) {\n wasOfflineRef.current = true;\n setFlashOnline(false);\n return;\n }\n if (!wasOfflineRef.current || onlineFlashMs <= 0) return;\n wasOfflineRef.current = false;\n setFlashOnline(true);\n const id = window.setTimeout(() => setFlashOnline(false), onlineFlashMs);\n return () => window.clearTimeout(id);\n }, [online, onlineFlashMs]);\n\n if (online && !flashOnline) return null;\n\n const showingOnline = online && flashOnline;\n return (\n <div\n className={cn(\n styles.bar,\n styles[position],\n showingOnline ? styles.online : styles.offline,\n className,\n )}\n role=\"status\"\n aria-live=\"polite\"\n {...props}\n >\n {children ?? (\n <>\n {showingOnline ? (\n <Wifi className={styles.icon} size={16} aria-hidden=\"true\" />\n ) : (\n <CloudOff className={styles.icon} size={16} aria-hidden=\"true\" />\n )}\n <span>{showingOnline ? onlineLabel : offlineLabel}</span>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAiB,EAC7B,cAAW,UACX,kBAAe,uEACf,iBAAc,0BACd,mBAAgB,KAChB,cACA,aACA,GAAG,KACmB;CACtB,IAAM,IAAS,EAAU,GACnB,CAAC,GAAa,KAAkB,EAAkB,EAAK,GACvD,IAAgB,EAAgB,EAAK;CAe3C,IAbA,QAAgB;EACZ,IAAI,CAAC,GAAQ;GAET,AADA,EAAc,UAAU,IACxB,EAAe,EAAK;GACpB;EACJ;EACA,IAAI,CAAC,EAAc,WAAW,KAAiB,GAAG;EAElD,AADA,EAAc,UAAU,IACxB,EAAe,EAAI;EACnB,IAAM,IAAK,OAAO,iBAAiB,EAAe,EAAK,GAAG,CAAa;EACvE,aAAa,OAAO,aAAa,CAAE;CACvC,GAAG,CAAC,GAAQ,CAAa,CAAC,GAEtB,KAAU,CAAC,GAAa,OAAO;CAEnC,IAAM,IAAgB,KAAU;CAChC,OACI,kBAAC,OAAD;EACI,WAAW,EACP,EAAO,KACP,EAAO,IACP,IAAgB,EAAO,SAAS,EAAO,SACvC,CACJ;EACA,MAAK;EACL,aAAU;EACV,GAAI;YAEH,KACG,kBAAA,GAAA,EAAA,UAAA,CAEQ,EADH,IACI,IAEA,GAFD;GAAM,WAAW,EAAO;GAAM,MAAM;GAAI,eAAY;EAAQ,CAEI,GAEpE,kBAAC,QAAD,EAAA,UAAO,IAAgB,IAAc,EAAmB,CAAA,CAC1D,EAAA,CAAA;CAEL,CAAA;AAEb"}
1
+ {"version":3,"file":"OfflineIndicator.js","names":[],"sources":["../../../src/components/OfflineIndicator/OfflineIndicator.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { CloudOff, Wifi } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { useOnline } from \"@/hooks\";\nimport styles from \"./OfflineIndicator.module.css\";\n\nexport type OfflineIndicatorPosition = \"top\" | \"bottom\";\n\nexport interface OfflineIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n /** Where to pin the fixed bar. Default `\"bottom\"`. */\n position?: OfflineIndicatorPosition;\n /** Message shown while offline. */\n offlineLabel?: ReactNode;\n /** Message flashed briefly when connectivity returns. */\n onlineLabel?: ReactNode;\n /**\n * How long (ms) to keep the \"back online\" flash visible. `0` hides it\n * entirely. Default `3000`.\n */\n onlineFlashMs?: number;\n /** Override the offline body entirely (icon + label ignored). */\n children?: ReactNode;\n}\n\n/**\n * Fixed bar that appears while the browser is offline and, by default, flashes\n * a brief confirmation when the connection returns. Backed by {@link useOnline}\n * — no props needed for the common case.\n *\n * Renders nothing while online (after any flash has elapsed), so it is safe to\n * mount unconditionally at the app root.\n *\n * @example\n * <OfflineIndicator position=\"top\" />\n */\nexport function OfflineIndicator({\n position = \"bottom\",\n offlineLabel = \"Você está offline. As alterações serão sincronizadas ao reconectar.\",\n onlineLabel = \"Conexão restabelecida.\",\n onlineFlashMs = 3000,\n className,\n children,\n ...props\n}: OfflineIndicatorProps) {\n const online = useOnline();\n const [flashOnline, setFlashOnline] = useState<boolean>(false);\n const wasOfflineRef = useRef<boolean>(false);\n\n useEffect(() => {\n if (!online) {\n wasOfflineRef.current = true;\n setFlashOnline(false);\n return;\n }\n if (!wasOfflineRef.current || onlineFlashMs <= 0) return;\n wasOfflineRef.current = false;\n setFlashOnline(true);\n const id = window.setTimeout(() => setFlashOnline(false), onlineFlashMs);\n return () => window.clearTimeout(id);\n }, [online, onlineFlashMs]);\n\n if (online && !flashOnline) return null;\n\n const showingOnline = online && flashOnline;\n return (\n <div\n className={cn(\n styles.bar,\n styles[position],\n showingOnline ? styles.online : styles.offline,\n className,\n )}\n role=\"status\"\n aria-live=\"polite\"\n {...props}\n >\n {children ?? (\n <>\n {showingOnline ? (\n <Wifi className={styles.icon} size={16} aria-hidden=\"true\" />\n ) : (\n <CloudOff className={styles.icon} size={16} aria-hidden=\"true\" />\n )}\n <span>{showingOnline ? onlineLabel : offlineLabel}</span>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAiB,EAC7B,cAAW,UACX,kBAAe,uEACf,iBAAc,0BACd,mBAAgB,KAChB,cACA,aACA,GAAG,KACmB;CACtB,IAAM,IAAS,EAAU,GACnB,CAAC,GAAa,KAAkB,EAAkB,EAAK,GACvD,IAAgB,EAAgB,EAAK;CAe3C,IAbA,QAAgB;EACZ,IAAI,CAAC,GAAQ;GAET,AADA,EAAc,UAAU,IACxB,EAAe,EAAK;GACpB;EACJ;EACA,IAAI,CAAC,EAAc,WAAW,KAAiB,GAAG;EAElD,AADA,EAAc,UAAU,IACxB,EAAe,EAAI;EACnB,IAAM,IAAK,OAAO,iBAAiB,EAAe,EAAK,GAAG,CAAa;EACvE,aAAa,OAAO,aAAa,CAAE;CACvC,GAAG,CAAC,GAAQ,CAAa,CAAC,GAEtB,KAAU,CAAC,GAAa,OAAO;CAEnC,IAAM,IAAgB,KAAU;CAChC,OACI,kBAAC,OAAD;EACI,WAAW,EACP,EAAO,KACP,EAAO,IACP,IAAgB,EAAO,SAAS,EAAO,SACvC,CACJ;EACA,MAAK;EACL,aAAU;EACV,GAAI;EAEH,UAAA,KACG,kBAAA,GAAA,EAAA,UAAA,CAEQ,EADH,IACI,IAEA,GAFD;GAAM,WAAW,EAAO;GAAM,MAAM;GAAI,eAAY;EAAQ,CAEI,GAEpE,kBAAC,QAAD,EAAA,UAAO,IAAgB,IAAc,EAAmB,CAAA,CAC1D,EAAA,CAAA;CAEL,CAAA;AAEb"}