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":"ToggleGroup.cjs","names":[],"sources":["../../../src/components/ToggleGroup/ToggleGroup.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./ToggleGroup.module.css\";\n\nexport type ToggleGroupType = \"single\" | \"multiple\";\n\ninterface ToggleGroupContextValue {\n type: ToggleGroupType;\n isSelected: (value: string) => boolean;\n toggle: (value: string) => void;\n}\n\nconst ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null);\n\nfunction useToggleGroupContext(): ToggleGroupContextValue {\n const ctx = useContext(ToggleGroupContext);\n if (!ctx) {\n throw new Error(\"ToggleGroupItem must be used within a ToggleGroup\");\n }\n return ctx;\n}\n\nexport interface ToggleGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Selection mode. `\"single\"` keeps one value; `\"multiple\"` keeps a set. Default `\"single\"`. */\n type?: ToggleGroupType;\n /** Controlled value — `string` for `single`, `string[]` for `multiple`. */\n value?: string | string[];\n /** Uncontrolled initial value. */\n defaultValue?: string | string[];\n /** Fired with the next value whenever the selection changes. */\n onValueChange?: (value: string | string[]) => void;\n children: ReactNode;\n}\n\nfunction normalizeArray(value: string | string[] | undefined): string[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * A set of {@link ToggleGroupItem}s that share selection state via context.\n *\n * In `single` mode the value is a `string` (empty string when nothing is\n * selected); in `multiple` mode it is a `string[]`. Works controlled (pass\n * `value` + `onValueChange`) or uncontrolled (pass `defaultValue`).\n *\n * @param props - {@link ToggleGroupProps}.\n * @returns A `role=\"group\"` container wrapping the items.\n */\nexport function ToggleGroup({\n type = \"single\",\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n ...props\n}: ToggleGroupProps) {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<string[]>(() =>\n normalizeArray(defaultValue),\n );\n const selected = isControlled ? normalizeArray(value) : internalValue;\n\n const commit = useCallback(\n (next: string[]): void => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(type === \"single\" ? (next[0] ?? \"\") : next);\n },\n [isControlled, onValueChange, type],\n );\n\n const toggle = useCallback(\n (itemValue: string): void => {\n const isOn = selected.includes(itemValue);\n if (type === \"single\") {\n commit(isOn ? [] : [itemValue]);\n } else {\n commit(isOn ? selected.filter((v) => v !== itemValue) : [...selected, itemValue]);\n }\n },\n [selected, type, commit],\n );\n\n const ctx = useMemo<ToggleGroupContextValue>(\n () => ({\n type,\n isSelected: (itemValue: string) => selected.includes(itemValue),\n toggle,\n }),\n [type, selected, toggle],\n );\n\n return (\n <ToggleGroupContext.Provider value={ctx}>\n <div role=\"group\" className={cn(styles.group, className)} {...props}>\n {children}\n </div>\n </ToggleGroupContext.Provider>\n );\n}\n\nexport interface ToggleGroupItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, \"onClick\"> {\n /** Stable value identifying this item within the group. */\n value: string;\n /** Disables the item. */\n disabled?: boolean;\n children: ReactNode;\n}\n\n/**\n * A single selectable item inside a {@link ToggleGroup}. Reads and updates the\n * group's shared state through context.\n *\n * Icon-only items need an explicit `aria-label` (forwarded to the button):\n * an SVG child gives screen readers nothing to announce.\n *\n * @param props - {@link ToggleGroupItemProps}.\n * @returns A `role=\"button\"` element exposing its state via `aria-pressed`.\n */\nexport function ToggleGroupItem({\n value,\n disabled,\n className,\n children,\n ...props\n}: ToggleGroupItemProps) {\n const { isSelected, toggle } = useToggleGroupContext();\n const selected = isSelected(value);\n\n return (\n <button\n type=\"button\"\n role=\"button\"\n aria-pressed={selected}\n disabled={disabled}\n data-state={selected ? \"on\" : \"off\"}\n className={cn(styles.item, selected && styles.selected, className)}\n onClick={() => toggle(value)}\n {...props}\n >\n {children}\n </button>\n );\n}\n"],"mappings":"kIAaA,IAAM,GAAA,EAAA,EAAA,cAAA,CAAmE,IAAI,EAE7E,SAAS,GAAiD,CACtD,IAAM,GAAA,EAAA,EAAA,WAAA,CAAiB,CAAkB,EACzC,GAAI,CAAC,EACD,MAAU,MAAM,mDAAmD,EAEvE,OAAO,CACX,CAcA,SAAS,EAAe,EAAgD,CAEpE,OADI,IAAU,IAAA,GAAkB,CAAC,EAC1B,MAAM,QAAQ,CAAK,EAAI,EAAQ,CAAC,CAAK,CAChD,CAYA,SAAgB,EAAY,CACxB,OAAO,SACP,QACA,eACA,gBACA,YACA,WACA,GAAG,GACc,CACjB,IAAM,EAAe,IAAU,IAAA,GACzB,CAAC,EAAe,IAAA,EAAA,EAAA,SAAA,KAClB,EAAe,CAAY,CAC/B,EACM,EAAW,EAAe,EAAe,CAAK,EAAI,EAElD,GAAA,EAAA,EAAA,YAAA,CACD,GAAyB,CACjB,GAAc,EAAiB,CAAI,EACxC,IAAgB,IAAS,SAAY,EAAK,IAAM,GAAM,CAAI,CAC9D,EACA,CAAC,EAAc,EAAe,CAAI,CACtC,EAEM,GAAA,EAAA,EAAA,YAAA,CACD,GAA4B,CACzB,IAAM,EAAO,EAAS,SAAS,CAAS,EAEpC,EADA,IAAS,SACF,EAAO,CAAC,EAAI,CAAC,CAAS,EAEtB,EAAO,EAAS,OAAQ,GAAM,IAAM,CAAS,EAAI,CAAC,GAAG,EAAU,CAAS,CAAC,CAExF,EACA,CAAC,EAAU,EAAM,CAAM,CAC3B,EAEM,GAAA,EAAA,EAAA,QAAA,MACK,CACH,OACA,WAAa,GAAsB,EAAS,SAAS,CAAS,EAC9D,QACJ,GACA,CAAC,EAAM,EAAU,CAAM,CAC3B,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,EAAmB,SAApB,CAA6B,MAAO,YAChC,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,KAAK,QAAQ,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,CAAS,EAAG,GAAI,EACzD,UACA,CAAA,CACoB,CAAA,CAErC,CAoBA,SAAgB,EAAgB,CAC5B,QACA,WACA,YACA,WACA,GAAG,GACkB,CACrB,GAAM,CAAE,aAAY,UAAW,EAAsB,EAC/C,EAAW,EAAW,CAAK,EAEjC,OACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAK,SACL,eAAc,EACJ,WACV,aAAY,EAAW,KAAO,MAC9B,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,GAAY,EAAA,QAAO,SAAU,CAAS,EACjE,YAAe,EAAO,CAAK,EAC3B,GAAI,EAEH,UACG,CAAA,CAEhB"}
1
+ {"version":3,"file":"ToggleGroup.cjs","names":[],"sources":["../../../src/components/ToggleGroup/ToggleGroup.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./ToggleGroup.module.css\";\n\nexport type ToggleGroupType = \"single\" | \"multiple\";\n\ninterface ToggleGroupContextValue {\n type: ToggleGroupType;\n isSelected: (value: string) => boolean;\n toggle: (value: string) => void;\n}\n\nconst ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null);\n\nfunction useToggleGroupContext(): ToggleGroupContextValue {\n const ctx = useContext(ToggleGroupContext);\n if (!ctx) {\n throw new Error(\"ToggleGroupItem must be used within a ToggleGroup\");\n }\n return ctx;\n}\n\nexport interface ToggleGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Selection mode. `\"single\"` keeps one value; `\"multiple\"` keeps a set. Default `\"single\"`. */\n type?: ToggleGroupType;\n /** Controlled value — `string` for `single`, `string[]` for `multiple`. */\n value?: string | string[];\n /** Uncontrolled initial value. */\n defaultValue?: string | string[];\n /** Fired with the next value whenever the selection changes. */\n onValueChange?: (value: string | string[]) => void;\n children: ReactNode;\n}\n\nfunction normalizeArray(value: string | string[] | undefined): string[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * A set of {@link ToggleGroupItem}s that share selection state via context.\n *\n * In `single` mode the value is a `string` (empty string when nothing is\n * selected); in `multiple` mode it is a `string[]`. Works controlled (pass\n * `value` + `onValueChange`) or uncontrolled (pass `defaultValue`).\n *\n * @param props - {@link ToggleGroupProps}.\n * @returns A `role=\"group\"` container wrapping the items.\n */\nexport function ToggleGroup({\n type = \"single\",\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n ...props\n}: ToggleGroupProps) {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<string[]>(() =>\n normalizeArray(defaultValue),\n );\n const selected = isControlled ? normalizeArray(value) : internalValue;\n\n const commit = useCallback(\n (next: string[]): void => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(type === \"single\" ? (next[0] ?? \"\") : next);\n },\n [isControlled, onValueChange, type],\n );\n\n const toggle = useCallback(\n (itemValue: string): void => {\n const isOn = selected.includes(itemValue);\n if (type === \"single\") {\n commit(isOn ? [] : [itemValue]);\n } else {\n commit(isOn ? selected.filter((v) => v !== itemValue) : [...selected, itemValue]);\n }\n },\n [selected, type, commit],\n );\n\n const ctx = useMemo<ToggleGroupContextValue>(\n () => ({\n type,\n isSelected: (itemValue: string) => selected.includes(itemValue),\n toggle,\n }),\n [type, selected, toggle],\n );\n\n return (\n <ToggleGroupContext.Provider value={ctx}>\n <div role=\"group\" className={cn(styles.group, className)} {...props}>\n {children}\n </div>\n </ToggleGroupContext.Provider>\n );\n}\n\nexport interface ToggleGroupItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, \"onClick\"> {\n /** Stable value identifying this item within the group. */\n value: string;\n /** Disables the item. */\n disabled?: boolean;\n children: ReactNode;\n}\n\n/**\n * A single selectable item inside a {@link ToggleGroup}. Reads and updates the\n * group's shared state through context.\n *\n * Icon-only items need an explicit `aria-label` (forwarded to the button):\n * an SVG child gives screen readers nothing to announce.\n *\n * @param props - {@link ToggleGroupItemProps}.\n * @returns A `role=\"button\"` element exposing its state via `aria-pressed`.\n */\nexport function ToggleGroupItem({\n value,\n disabled,\n className,\n children,\n ...props\n}: ToggleGroupItemProps) {\n const { isSelected, toggle } = useToggleGroupContext();\n const selected = isSelected(value);\n\n return (\n <button\n type=\"button\"\n role=\"button\"\n aria-pressed={selected}\n disabled={disabled}\n data-state={selected ? \"on\" : \"off\"}\n className={cn(styles.item, selected && styles.selected, className)}\n onClick={() => toggle(value)}\n {...props}\n >\n {children}\n </button>\n );\n}\n"],"mappings":"kIAaA,IAAM,GAAA,EAAqB,EAAA,cAAA,CAA8C,IAAI,EAE7E,SAAS,GAAiD,CACtD,IAAM,GAAA,EAAM,EAAA,WAAA,CAAW,CAAkB,EACzC,GAAI,CAAC,EACD,MAAU,MAAM,mDAAmD,EAEvE,OAAO,CACX,CAcA,SAAS,EAAe,EAAgD,CAEpE,OADI,IAAU,IAAA,GAAkB,CAAC,EAC1B,MAAM,QAAQ,CAAK,EAAI,EAAQ,CAAC,CAAK,CAChD,CAYA,SAAgB,EAAY,CACxB,OAAO,SACP,QACA,eACA,gBACA,YACA,WACA,GAAG,GACc,CACjB,IAAM,EAAe,IAAU,IAAA,GACzB,CAAC,EAAe,IAAA,EAAoB,EAAA,SAAA,KACtC,EAAe,CAAY,CAC/B,EACM,EAAW,EAAe,EAAe,CAAK,EAAI,EAElD,GAAA,EAAS,EAAA,YAAA,CACV,GAAyB,CACjB,GAAc,EAAiB,CAAI,EACxC,IAAgB,IAAS,SAAY,EAAK,IAAM,GAAM,CAAI,CAC9D,EACA,CAAC,EAAc,EAAe,CAAI,CACtC,EAEM,GAAA,EAAS,EAAA,YAAA,CACV,GAA4B,CACzB,IAAM,EAAO,EAAS,SAAS,CAAS,EAEpC,EADA,IAAS,SACF,EAAO,CAAC,EAAI,CAAC,CAAS,EAEtB,EAAO,EAAS,OAAQ,GAAM,IAAM,CAAS,EAAI,CAAC,GAAG,EAAU,CAAS,CAAC,CAExF,EACA,CAAC,EAAU,EAAM,CAAM,CAC3B,EAEM,GAAA,EAAM,EAAA,QAAA,MACD,CACH,OACA,WAAa,GAAsB,EAAS,SAAS,CAAS,EAC9D,QACJ,GACA,CAAC,EAAM,EAAU,CAAM,CAC3B,EAEA,OACI,EAAA,EAAA,IAAA,CAAC,EAAmB,SAApB,CAA6B,MAAO,EAChC,UAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,KAAK,QAAQ,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,CAAS,EAAG,GAAI,EACzD,UACA,CAAA,CACoB,CAAA,CAErC,CAoBA,SAAgB,EAAgB,CAC5B,QACA,WACA,YACA,WACA,GAAG,GACkB,CACrB,GAAM,CAAE,aAAY,UAAW,EAAsB,EAC/C,EAAW,EAAW,CAAK,EAEjC,OACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,KAAK,SACL,eAAc,EACJ,WACV,aAAY,EAAW,KAAO,MAC9B,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,GAAY,EAAA,QAAO,SAAU,CAAS,EACjE,YAAe,EAAO,CAAK,EAC3B,GAAI,EAEH,UACG,CAAA,CAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ToggleGroup.js","names":[],"sources":["../../../src/components/ToggleGroup/ToggleGroup.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./ToggleGroup.module.css\";\n\nexport type ToggleGroupType = \"single\" | \"multiple\";\n\ninterface ToggleGroupContextValue {\n type: ToggleGroupType;\n isSelected: (value: string) => boolean;\n toggle: (value: string) => void;\n}\n\nconst ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null);\n\nfunction useToggleGroupContext(): ToggleGroupContextValue {\n const ctx = useContext(ToggleGroupContext);\n if (!ctx) {\n throw new Error(\"ToggleGroupItem must be used within a ToggleGroup\");\n }\n return ctx;\n}\n\nexport interface ToggleGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Selection mode. `\"single\"` keeps one value; `\"multiple\"` keeps a set. Default `\"single\"`. */\n type?: ToggleGroupType;\n /** Controlled value — `string` for `single`, `string[]` for `multiple`. */\n value?: string | string[];\n /** Uncontrolled initial value. */\n defaultValue?: string | string[];\n /** Fired with the next value whenever the selection changes. */\n onValueChange?: (value: string | string[]) => void;\n children: ReactNode;\n}\n\nfunction normalizeArray(value: string | string[] | undefined): string[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * A set of {@link ToggleGroupItem}s that share selection state via context.\n *\n * In `single` mode the value is a `string` (empty string when nothing is\n * selected); in `multiple` mode it is a `string[]`. Works controlled (pass\n * `value` + `onValueChange`) or uncontrolled (pass `defaultValue`).\n *\n * @param props - {@link ToggleGroupProps}.\n * @returns A `role=\"group\"` container wrapping the items.\n */\nexport function ToggleGroup({\n type = \"single\",\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n ...props\n}: ToggleGroupProps) {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<string[]>(() =>\n normalizeArray(defaultValue),\n );\n const selected = isControlled ? normalizeArray(value) : internalValue;\n\n const commit = useCallback(\n (next: string[]): void => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(type === \"single\" ? (next[0] ?? \"\") : next);\n },\n [isControlled, onValueChange, type],\n );\n\n const toggle = useCallback(\n (itemValue: string): void => {\n const isOn = selected.includes(itemValue);\n if (type === \"single\") {\n commit(isOn ? [] : [itemValue]);\n } else {\n commit(isOn ? selected.filter((v) => v !== itemValue) : [...selected, itemValue]);\n }\n },\n [selected, type, commit],\n );\n\n const ctx = useMemo<ToggleGroupContextValue>(\n () => ({\n type,\n isSelected: (itemValue: string) => selected.includes(itemValue),\n toggle,\n }),\n [type, selected, toggle],\n );\n\n return (\n <ToggleGroupContext.Provider value={ctx}>\n <div role=\"group\" className={cn(styles.group, className)} {...props}>\n {children}\n </div>\n </ToggleGroupContext.Provider>\n );\n}\n\nexport interface ToggleGroupItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, \"onClick\"> {\n /** Stable value identifying this item within the group. */\n value: string;\n /** Disables the item. */\n disabled?: boolean;\n children: ReactNode;\n}\n\n/**\n * A single selectable item inside a {@link ToggleGroup}. Reads and updates the\n * group's shared state through context.\n *\n * Icon-only items need an explicit `aria-label` (forwarded to the button):\n * an SVG child gives screen readers nothing to announce.\n *\n * @param props - {@link ToggleGroupItemProps}.\n * @returns A `role=\"button\"` element exposing its state via `aria-pressed`.\n */\nexport function ToggleGroupItem({\n value,\n disabled,\n className,\n children,\n ...props\n}: ToggleGroupItemProps) {\n const { isSelected, toggle } = useToggleGroupContext();\n const selected = isSelected(value);\n\n return (\n <button\n type=\"button\"\n role=\"button\"\n aria-pressed={selected}\n disabled={disabled}\n data-state={selected ? \"on\" : \"off\"}\n className={cn(styles.item, selected && styles.selected, className)}\n onClick={() => toggle(value)}\n {...props}\n >\n {children}\n </button>\n );\n}\n"],"mappings":";;;;;AAaA,IAAM,IAAqB,EAA8C,IAAI;AAE7E,SAAS,IAAiD;CACtD,IAAM,IAAM,EAAW,CAAkB;CACzC,IAAI,CAAC,GACD,MAAU,MAAM,mDAAmD;CAEvE,OAAO;AACX;AAcA,SAAS,EAAe,GAAgD;CAEpE,OADI,MAAU,KAAA,IAAkB,CAAC,IAC1B,MAAM,QAAQ,CAAK,IAAI,IAAQ,CAAC,CAAK;AAChD;AAYA,SAAgB,EAAY,EACxB,UAAO,UACP,UACA,iBACA,kBACA,cACA,aACA,GAAG,KACc;CACjB,IAAM,IAAe,MAAU,KAAA,GACzB,CAAC,GAAe,KAAoB,QACtC,EAAe,CAAY,CAC/B,GACM,IAAW,IAAe,EAAe,CAAK,IAAI,GAElD,IAAS,GACV,MAAyB;EAEtB,AADK,KAAc,EAAiB,CAAI,GACxC,IAAgB,MAAS,WAAY,EAAK,MAAM,KAAM,CAAI;CAC9D,GACA;EAAC;EAAc;EAAe;CAAI,CACtC,GAEM,IAAS,GACV,MAA4B;EACzB,IAAM,IAAO,EAAS,SAAS,CAAS;EACxC,AACI,EADA,MAAS,WACF,IAAO,CAAC,IAAI,CAAC,CAAS,IAEtB,IAAO,EAAS,QAAQ,MAAM,MAAM,CAAS,IAAI,CAAC,GAAG,GAAU,CAAS,CAAC;CAExF,GACA;EAAC;EAAU;EAAM;CAAM,CAC3B,GAEM,IAAM,SACD;EACH;EACA,aAAa,MAAsB,EAAS,SAAS,CAAS;EAC9D;CACJ,IACA;EAAC;EAAM;EAAU;CAAM,CAC3B;CAEA,OACI,kBAAC,EAAmB,UAApB;EAA6B,OAAO;YAChC,kBAAC,OAAD;GAAK,MAAK;GAAQ,WAAW,EAAG,EAAO,OAAO,CAAS;GAAG,GAAI;GACzD;EACA,CAAA;CACoB,CAAA;AAErC;AAoBA,SAAgB,EAAgB,EAC5B,UACA,aACA,cACA,aACA,GAAG,KACkB;CACrB,IAAM,EAAE,eAAY,cAAW,EAAsB,GAC/C,IAAW,EAAW,CAAK;CAEjC,OACI,kBAAC,UAAD;EACI,MAAK;EACL,MAAK;EACL,gBAAc;EACJ;EACV,cAAY,IAAW,OAAO;EAC9B,WAAW,EAAG,EAAO,MAAM,KAAY,EAAO,UAAU,CAAS;EACjE,eAAe,EAAO,CAAK;EAC3B,GAAI;EAEH;CACG,CAAA;AAEhB"}
1
+ {"version":3,"file":"ToggleGroup.js","names":[],"sources":["../../../src/components/ToggleGroup/ToggleGroup.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useMemo, useState } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./ToggleGroup.module.css\";\n\nexport type ToggleGroupType = \"single\" | \"multiple\";\n\ninterface ToggleGroupContextValue {\n type: ToggleGroupType;\n isSelected: (value: string) => boolean;\n toggle: (value: string) => void;\n}\n\nconst ToggleGroupContext = createContext<ToggleGroupContextValue | null>(null);\n\nfunction useToggleGroupContext(): ToggleGroupContextValue {\n const ctx = useContext(ToggleGroupContext);\n if (!ctx) {\n throw new Error(\"ToggleGroupItem must be used within a ToggleGroup\");\n }\n return ctx;\n}\n\nexport interface ToggleGroupProps extends HTMLAttributes<HTMLDivElement> {\n /** Selection mode. `\"single\"` keeps one value; `\"multiple\"` keeps a set. Default `\"single\"`. */\n type?: ToggleGroupType;\n /** Controlled value — `string` for `single`, `string[]` for `multiple`. */\n value?: string | string[];\n /** Uncontrolled initial value. */\n defaultValue?: string | string[];\n /** Fired with the next value whenever the selection changes. */\n onValueChange?: (value: string | string[]) => void;\n children: ReactNode;\n}\n\nfunction normalizeArray(value: string | string[] | undefined): string[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * A set of {@link ToggleGroupItem}s that share selection state via context.\n *\n * In `single` mode the value is a `string` (empty string when nothing is\n * selected); in `multiple` mode it is a `string[]`. Works controlled (pass\n * `value` + `onValueChange`) or uncontrolled (pass `defaultValue`).\n *\n * @param props - {@link ToggleGroupProps}.\n * @returns A `role=\"group\"` container wrapping the items.\n */\nexport function ToggleGroup({\n type = \"single\",\n value,\n defaultValue,\n onValueChange,\n className,\n children,\n ...props\n}: ToggleGroupProps) {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<string[]>(() =>\n normalizeArray(defaultValue),\n );\n const selected = isControlled ? normalizeArray(value) : internalValue;\n\n const commit = useCallback(\n (next: string[]): void => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(type === \"single\" ? (next[0] ?? \"\") : next);\n },\n [isControlled, onValueChange, type],\n );\n\n const toggle = useCallback(\n (itemValue: string): void => {\n const isOn = selected.includes(itemValue);\n if (type === \"single\") {\n commit(isOn ? [] : [itemValue]);\n } else {\n commit(isOn ? selected.filter((v) => v !== itemValue) : [...selected, itemValue]);\n }\n },\n [selected, type, commit],\n );\n\n const ctx = useMemo<ToggleGroupContextValue>(\n () => ({\n type,\n isSelected: (itemValue: string) => selected.includes(itemValue),\n toggle,\n }),\n [type, selected, toggle],\n );\n\n return (\n <ToggleGroupContext.Provider value={ctx}>\n <div role=\"group\" className={cn(styles.group, className)} {...props}>\n {children}\n </div>\n </ToggleGroupContext.Provider>\n );\n}\n\nexport interface ToggleGroupItemProps extends Omit<HTMLAttributes<HTMLButtonElement>, \"onClick\"> {\n /** Stable value identifying this item within the group. */\n value: string;\n /** Disables the item. */\n disabled?: boolean;\n children: ReactNode;\n}\n\n/**\n * A single selectable item inside a {@link ToggleGroup}. Reads and updates the\n * group's shared state through context.\n *\n * Icon-only items need an explicit `aria-label` (forwarded to the button):\n * an SVG child gives screen readers nothing to announce.\n *\n * @param props - {@link ToggleGroupItemProps}.\n * @returns A `role=\"button\"` element exposing its state via `aria-pressed`.\n */\nexport function ToggleGroupItem({\n value,\n disabled,\n className,\n children,\n ...props\n}: ToggleGroupItemProps) {\n const { isSelected, toggle } = useToggleGroupContext();\n const selected = isSelected(value);\n\n return (\n <button\n type=\"button\"\n role=\"button\"\n aria-pressed={selected}\n disabled={disabled}\n data-state={selected ? \"on\" : \"off\"}\n className={cn(styles.item, selected && styles.selected, className)}\n onClick={() => toggle(value)}\n {...props}\n >\n {children}\n </button>\n );\n}\n"],"mappings":";;;;;AAaA,IAAM,IAAqB,EAA8C,IAAI;AAE7E,SAAS,IAAiD;CACtD,IAAM,IAAM,EAAW,CAAkB;CACzC,IAAI,CAAC,GACD,MAAU,MAAM,mDAAmD;CAEvE,OAAO;AACX;AAcA,SAAS,EAAe,GAAgD;CAEpE,OADI,MAAU,KAAA,IAAkB,CAAC,IAC1B,MAAM,QAAQ,CAAK,IAAI,IAAQ,CAAC,CAAK;AAChD;AAYA,SAAgB,EAAY,EACxB,UAAO,UACP,UACA,iBACA,kBACA,cACA,aACA,GAAG,KACc;CACjB,IAAM,IAAe,MAAU,KAAA,GACzB,CAAC,GAAe,KAAoB,QACtC,EAAe,CAAY,CAC/B,GACM,IAAW,IAAe,EAAe,CAAK,IAAI,GAElD,IAAS,GACV,MAAyB;EAEtB,AADK,KAAc,EAAiB,CAAI,GACxC,IAAgB,MAAS,WAAY,EAAK,MAAM,KAAM,CAAI;CAC9D,GACA;EAAC;EAAc;EAAe;CAAI,CACtC,GAEM,IAAS,GACV,MAA4B;EACzB,IAAM,IAAO,EAAS,SAAS,CAAS;EACxC,AACI,EADA,MAAS,WACF,IAAO,CAAC,IAAI,CAAC,CAAS,IAEtB,IAAO,EAAS,QAAQ,MAAM,MAAM,CAAS,IAAI,CAAC,GAAG,GAAU,CAAS,CAAC;CAExF,GACA;EAAC;EAAU;EAAM;CAAM,CAC3B,GAEM,IAAM,SACD;EACH;EACA,aAAa,MAAsB,EAAS,SAAS,CAAS;EAC9D;CACJ,IACA;EAAC;EAAM;EAAU;CAAM,CAC3B;CAEA,OACI,kBAAC,EAAmB,UAApB;EAA6B,OAAO;EAChC,UAAA,kBAAC,OAAD;GAAK,MAAK;GAAQ,WAAW,EAAG,EAAO,OAAO,CAAS;GAAG,GAAI;GACzD;EACA,CAAA;CACoB,CAAA;AAErC;AAoBA,SAAgB,EAAgB,EAC5B,UACA,aACA,cACA,aACA,GAAG,KACkB;CACrB,IAAM,EAAE,eAAY,cAAW,EAAsB,GAC/C,IAAW,EAAW,CAAK;CAEjC,OACI,kBAAC,UAAD;EACI,MAAK;EACL,MAAK;EACL,gBAAc;EACJ;EACV,cAAY,IAAW,OAAO;EAC9B,WAAW,EAAG,EAAO,MAAM,KAAY,EAAO,UAAU,CAAS;EACjE,eAAe,EAAO,CAAK;EAC3B,GAAI;EAEH;CACG,CAAA;AAEhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.cjs","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n /**\n * Handle of the pending open timer.\n *\n * A ref, not a `let` in the component body: that binding is recreated on every\n * render, so any re-render between `show` and `hide` (a parent update, a state\n * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n * nothing — the tooltip then opened after the pointer had already left.\n */\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n function clearTimer(): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n\n function show(): void {\n if (disabled) return;\n clearTimer();\n timerRef.current = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n clearTimer();\n setOpen(false);\n }\n\n useEffect(() => {\n return () => {\n clearTimer();\n };\n }, []);\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"8HA2BA,SAAgB,EAAQ,CACpB,UACA,YAAY,MACZ,WACA,WAAW,GACX,YAAY,KACC,CACb,GAAM,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA6B,EAAK,EACzC,GAAA,EAAA,EAAA,MAAA,CAAkB,EASlB,GAAA,EAAA,EAAA,OAAA,CAAwD,IAAI,EAElE,SAAS,GAAmB,CACpB,EAAS,UAAY,OACrB,aAAa,EAAS,OAAO,EAC7B,EAAS,QAAU,KAE3B,CAEA,SAAS,GAAa,CACd,IACJ,EAAW,EACX,EAAS,QAAU,eAAiB,EAAQ,EAAI,EAAG,CAAS,EAChE,CAEA,SAAS,GAAa,CAClB,EAAW,EACX,EAAQ,EAAK,CACjB,EAEA,EAAA,EAAA,UAAA,SACiB,CACT,EAAW,CACf,EACD,CAAC,CAAC,EAEL,IAAM,GAAA,EAAA,EAAA,aAAA,CAAuB,EAAU,CACnC,aAAc,EACd,aAAc,EACd,QAAS,EACT,OAAQ,EACR,mBAAoB,EAAO,EAAY,IAAA,EAC3C,CAA4B,EAE5B,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,iBAAxB,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,GAAI,EACJ,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,EAAA,QAAO,EAAU,WAE7C,CACC,CAAA,CAER,GAEd"}
1
+ {"version":3,"file":"Tooltip.cjs","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n /**\n * Handle of the pending open timer.\n *\n * A ref, not a `let` in the component body: that binding is recreated on every\n * render, so any re-render between `show` and `hide` (a parent update, a state\n * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n * nothing — the tooltip then opened after the pointer had already left.\n */\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n function clearTimer(): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n\n function show(): void {\n if (disabled) return;\n clearTimer();\n timerRef.current = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n clearTimer();\n setOpen(false);\n }\n\n useEffect(() => {\n return () => {\n clearTimer();\n };\n }, []);\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"8HA2BA,SAAgB,EAAQ,CACpB,UACA,YAAY,MACZ,WACA,WAAW,GACX,YAAY,KACC,CACb,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAkB,EAAK,EACzC,GAAA,EAAY,EAAA,MAAA,CAAM,EASlB,GAAA,EAAW,EAAA,OAAA,CAA6C,IAAI,EAElE,SAAS,GAAmB,CACpB,EAAS,UAAY,OACrB,aAAa,EAAS,OAAO,EAC7B,EAAS,QAAU,KAE3B,CAEA,SAAS,GAAa,CACd,IACJ,EAAW,EACX,EAAS,QAAU,eAAiB,EAAQ,EAAI,EAAG,CAAS,EAChE,CAEA,SAAS,GAAa,CAClB,EAAW,EACX,EAAQ,EAAK,CACjB,EAEA,EAAA,EAAA,UAAA,SACiB,CACT,EAAW,CACf,EACD,CAAC,CAAC,EAEL,IAAM,GAAA,EAAU,EAAA,aAAA,CAAa,EAAU,CACnC,aAAc,EACd,aAAc,EACd,QAAS,EACT,OAAQ,EACR,mBAAoB,EAAO,EAAY,IAAA,EAC3C,CAA4B,EAE5B,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,QAAxB,SAAA,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,GAAI,EACJ,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,EAAA,QAAO,EAAU,EAE7C,SAAA,CACC,CAAA,CAER,GAEd"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n /**\n * Handle of the pending open timer.\n *\n * A ref, not a `let` in the component body: that binding is recreated on every\n * render, so any re-render between `show` and `hide` (a parent update, a state\n * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n * nothing — the tooltip then opened after the pointer had already left.\n */\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n function clearTimer(): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n\n function show(): void {\n if (disabled) return;\n clearTimer();\n timerRef.current = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n clearTimer();\n setOpen(false);\n }\n\n useEffect(() => {\n return () => {\n clearTimer();\n };\n }, []);\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AA2BA,SAAgB,EAAQ,EACpB,YACA,eAAY,OACZ,aACA,cAAW,IACX,eAAY,OACC;CACb,IAAM,CAAC,GAAM,KAAW,EAAkB,EAAK,GACzC,IAAY,EAAM,GASlB,IAAW,EAA6C,IAAI;CAElE,SAAS,IAAmB;EACxB,AAAI,EAAS,YAAY,SACrB,aAAa,EAAS,OAAO,GAC7B,EAAS,UAAU;CAE3B;CAEA,SAAS,IAAa;EACd,MACJ,EAAW,GACX,EAAS,UAAU,iBAAiB,EAAQ,EAAI,GAAG,CAAS;CAChE;CAEA,SAAS,IAAa;EAElB,AADA,EAAW,GACX,EAAQ,EAAK;CACjB;CAEA,cACiB;EACT,EAAW;CACf,GACD,CAAC,CAAC;CAEL,IAAM,IAAU,EAAa,GAAU;EACnC,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EACR,oBAAoB,IAAO,IAAY,KAAA;CAC3C,CAA4B;CAE5B,OACI,kBAAC,QAAD;EAAM,WAAW,EAAO;YAAxB,CACK,GACA,KACG,kBAAC,QAAD;GACI,IAAI;GACJ,MAAK;GACL,WAAW,EAAG,EAAO,QAAQ,EAAO,EAAU;aAE7C;EACC,CAAA,CAER;;AAEd"}
1
+ {"version":3,"file":"Tooltip.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n /**\n * Handle of the pending open timer.\n *\n * A ref, not a `let` in the component body: that binding is recreated on every\n * render, so any re-render between `show` and `hide` (a parent update, a state\n * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n * nothing — the tooltip then opened after the pointer had already left.\n */\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n function clearTimer(): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n\n function show(): void {\n if (disabled) return;\n clearTimer();\n timerRef.current = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n clearTimer();\n setOpen(false);\n }\n\n useEffect(() => {\n return () => {\n clearTimer();\n };\n }, []);\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AA2BA,SAAgB,EAAQ,EACpB,YACA,eAAY,OACZ,aACA,cAAW,IACX,eAAY,OACC;CACb,IAAM,CAAC,GAAM,KAAW,EAAkB,EAAK,GACzC,IAAY,EAAM,GASlB,IAAW,EAA6C,IAAI;CAElE,SAAS,IAAmB;EACxB,AAAI,EAAS,YAAY,SACrB,aAAa,EAAS,OAAO,GAC7B,EAAS,UAAU;CAE3B;CAEA,SAAS,IAAa;EACd,MACJ,EAAW,GACX,EAAS,UAAU,iBAAiB,EAAQ,EAAI,GAAG,CAAS;CAChE;CAEA,SAAS,IAAa;EAElB,AADA,EAAW,GACX,EAAQ,EAAK;CACjB;CAEA,cACiB;EACT,EAAW;CACf,GACD,CAAC,CAAC;CAEL,IAAM,IAAU,EAAa,GAAU;EACnC,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EACR,oBAAoB,IAAO,IAAY,KAAA;CAC3C,CAA4B;CAE5B,OACI,kBAAC,QAAD;EAAM,WAAW,EAAO;EAAxB,UAAA,CACK,GACA,KACG,kBAAC,QAAD;GACI,IAAI;GACJ,MAAK;GACL,WAAW,EAAG,EAAO,QAAQ,EAAO,EAAU;GAE7C,UAAA;EACC,CAAA,CAER;;AAEd"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tour.cjs","names":[],"sources":["../../../src/components/Tour/Tour.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — a product tour is a\n * controlled sequence (steps, index, onIndexChange, open, onClose, onFinish) drawn\n * over someone else's DOM (spotlightPadding, locale, className). The body measures\n * the target element, positions the spotlight and the card against it, and re-\n * measures on scroll and resize — one geometry, three triggers.\n */\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { cn } from \"@/utils/cn\";\n\nimport { backdropRects, placeCard, type TourPlacement, type TourRect } from \"./tour-position\";\nimport styles from \"./Tour.module.css\";\n\n/** One stop of the tour. */\nexport interface TourStep {\n /**\n * CSS selector of the element to point at.\n *\n * A selector rather than a ref, so a tour can be declared as data — in a config\n * file, from the backend, next to the copy — without every screen having to\n * thread refs up to whoever renders the tour.\n */\n target?: string;\n /** Heading of the card. */\n title?: ReactNode;\n /** The explanation. */\n body: ReactNode;\n /** Preferred side. Default `\"bottom\"`, flipped when it does not fit. */\n placement?: TourPlacement;\n}\n\n/** Labels, per locale. */\ninterface TourStrings {\n next: string;\n back: string;\n finish: string;\n skip: string;\n close: string;\n progress: (current: number, total: number) => string;\n}\n\nconst PT_BR: TourStrings = {\n next: \"Próximo\",\n back: \"Voltar\",\n finish: \"Concluir\",\n skip: \"Pular\",\n close: \"Fechar tour\",\n progress: (current, total) => `Passo ${current} de ${total}`,\n};\n\nconst EN: TourStrings = {\n next: \"Next\",\n back: \"Back\",\n finish: \"Done\",\n skip: \"Skip\",\n close: \"Close tour\",\n progress: (current, total) => `Step ${current} of ${total}`,\n};\n\nexport interface TourProps {\n /** The stops, in order. */\n steps: readonly TourStep[];\n /** Whether the tour is showing. Controlled. */\n open: boolean;\n /** Closed by `Esc`, by the close button or by \"skip\". */\n onClose: () => void;\n /** Called after the last step is confirmed. `onClose` follows it. */\n onFinish?: () => void;\n /** Current step index, when the app wants to drive it. */\n index?: number;\n /** Step changed — required only when `index` is given. */\n onIndexChange?: (index: number) => void;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Space kept clear around the highlighted element, in px. Default 4. */\n spotlightPadding?: number;\n /** Extra class on the card. */\n className?: string;\n}\n\n/**\n * A guided tour: dim the page, highlight one element at a time, explain it.\n *\n * The highlighted element stays **clickable** while everything else is blocked,\n * because the useful kind of coachmark says \"press this\" and lets you press it. That\n * is why the backdrop is four rectangles around the target rather than one overlay\n * with a `box-shadow` hole — a shadow is not hit-testable, so a hole made that way\n * would not block anything.\n *\n * Whether a user has seen the tour is the app's business: this component takes\n * `open` and emits `onClose`/`onFinish`. Persisting a flag in `localStorage` is one\n * line in the app and a wrong default here.\n *\n * @example\n * const [open, setOpen] = useState(!storage.get(\"tour-v1\"));\n *\n * <Tour\n * open={open}\n * steps={[\n * { target: \"#novo-pedido\", title: \"Comece aqui\", body: \"Todo pedido nasce deste botão.\" },\n * { target: \"[data-tour='filtros']\", body: \"E filtre por período aqui.\" },\n * ]}\n * onClose={() => setOpen(false)}\n * onFinish={() => storage.set(\"tour-v1\", true)}\n * />\n */\nexport function Tour({\n steps,\n open,\n onClose,\n onFinish,\n index,\n onIndexChange,\n locale = \"pt-BR\",\n spotlightPadding = 4,\n className,\n}: TourProps) {\n const strings = locale === \"en\" ? EN : PT_BR;\n const [internal, setInternal] = useState(0);\n const current = Math.min(index ?? internal, Math.max(0, steps.length - 1));\n const step = steps[current];\n\n const card = useRef<HTMLDivElement | null>(null);\n const [target, setTarget] = useState<TourRect | null>(null);\n const [position, setPosition] = useState<{\n placement: TourPlacement;\n top: number;\n left: number;\n }>({ placement: \"center\", top: 0, left: 0 });\n const [viewport, setViewport] = useState({ width: 0, height: 0 });\n const titleId = useId();\n const bodyId = useId();\n\n useFocusTrap(card, open);\n\n const goTo = useCallback(\n (next: number) => {\n if (index === undefined) setInternal(next);\n onIndexChange?.(next);\n },\n [index, onIndexChange],\n );\n\n /** Reset to the first step whenever the tour opens again. */\n useEffect(() => {\n if (open && index === undefined) setInternal(0);\n }, [open, index]);\n\n /**\n * Find the step's element, bring it into view, and measure everything.\n *\n * The scroll happens before measuring, and the measurement is deferred to the\n * next frame: reading a rect in the same tick as `scrollIntoView` gives the\n * position the element had *before* the scroll, which lands the card over the\n * wrong part of the page.\n */\n const measure = useCallback(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n const rect = element?.getBoundingClientRect() ?? null;\n setTarget(\n rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n );\n\n const size = {\n width: card.current?.offsetWidth ?? 320,\n height: card.current?.offsetHeight ?? 160,\n };\n const view = { width: window.innerWidth, height: window.innerHeight };\n setViewport(view);\n setPosition(\n placeCard({\n target: rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n card: size,\n viewport: view,\n preferred: step.placement,\n }),\n );\n }, [open, step]);\n\n useLayoutEffect(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n element?.scrollIntoView({ block: \"center\", inline: \"nearest\", behavior: \"smooth\" });\n const frame = requestAnimationFrame(measure);\n return () => cancelAnimationFrame(frame);\n }, [open, step, measure]);\n\n useEffect(() => {\n if (!open) return;\n window.addEventListener(\"resize\", measure);\n window.addEventListener(\"scroll\", measure, true);\n return () => {\n window.removeEventListener(\"resize\", measure);\n window.removeEventListener(\"scroll\", measure, true);\n };\n }, [open, measure]);\n\n useEffect(() => {\n if (open) card.current?.focus();\n }, [open, current]);\n\n if (!open || !step) return null;\n\n const last = current === steps.length - 1;\n\n const finish = (): void => {\n onFinish?.();\n onClose();\n };\n\n /**\n * Arrow keys walk the tour and `Esc` leaves it.\n *\n * `Esc` is handled here rather than on `window` so a tour opened over a modal\n * does not close both: the innermost handler that sees the key wins, and the\n * card has focus.\n */\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onClose();\n return;\n }\n if (event.key === \"ArrowRight\" && !last) {\n event.preventDefault();\n goTo(current + 1);\n }\n if (event.key === \"ArrowLeft\" && current > 0) {\n event.preventDefault();\n goTo(current - 1);\n }\n };\n\n return (\n <div className={styles.root} data-placement={position.placement}>\n {backdropRects(target, viewport, spotlightPadding).map((rect, i) => (\n <div\n key={i}\n className={styles.backdrop}\n style={{\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }}\n onClick={onClose}\n />\n ))}\n\n {target && (\n <div\n className={styles.spotlight}\n style={{\n top: target.top - spotlightPadding,\n left: target.left - spotlightPadding,\n width: target.width + spotlightPadding * 2,\n height: target.height + spotlightPadding * 2,\n }}\n />\n )}\n\n <div\n ref={card}\n className={cn(styles.card, className)}\n style={{ top: position.top, left: position.left }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={step.title ? titleId : undefined}\n aria-describedby={bodyId}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n <div className={styles.header}>\n <span className={styles.progress}>\n {strings.progress(current + 1, steps.length)}\n </span>\n <button\n type=\"button\"\n className={styles.close}\n aria-label={strings.close}\n onClick={onClose}\n >\n ×\n </button>\n </div>\n\n {step.title && (\n <h2 className={styles.title} id={titleId}>\n {step.title}\n </h2>\n )}\n <div className={styles.body} id={bodyId}>\n {step.body}\n </div>\n\n <div className={styles.footer}>\n {!last && (\n <button type=\"button\" className={styles.skip} onClick={onClose}>\n {strings.skip}\n </button>\n )}\n <div className={styles.actions}>\n {current > 0 && (\n <button\n type=\"button\"\n className={styles.secondary}\n onClick={() => goTo(current - 1)}\n >\n {strings.back}\n </button>\n )}\n <button\n type=\"button\"\n className={styles.primary}\n onClick={() => (last ? finish() : goTo(current + 1))}\n >\n {last ? strings.finish : strings.next}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tour.cjs","names":[],"sources":["../../../src/components/Tour/Tour.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — a product tour is a\n * controlled sequence (steps, index, onIndexChange, open, onClose, onFinish) drawn\n * over someone else's DOM (spotlightPadding, locale, className). The body measures\n * the target element, positions the spotlight and the card against it, and re-\n * measures on scroll and resize — one geometry, three triggers.\n */\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { cn } from \"@/utils/cn\";\n\nimport { backdropRects, placeCard, type TourPlacement, type TourRect } from \"./tour-position\";\nimport styles from \"./Tour.module.css\";\n\n/** One stop of the tour. */\nexport interface TourStep {\n /**\n * CSS selector of the element to point at.\n *\n * A selector rather than a ref, so a tour can be declared as data — in a config\n * file, from the backend, next to the copy — without every screen having to\n * thread refs up to whoever renders the tour.\n */\n target?: string;\n /** Heading of the card. */\n title?: ReactNode;\n /** The explanation. */\n body: ReactNode;\n /** Preferred side. Default `\"bottom\"`, flipped when it does not fit. */\n placement?: TourPlacement;\n}\n\n/** Labels, per locale. */\ninterface TourStrings {\n next: string;\n back: string;\n finish: string;\n skip: string;\n close: string;\n progress: (current: number, total: number) => string;\n}\n\nconst PT_BR: TourStrings = {\n next: \"Próximo\",\n back: \"Voltar\",\n finish: \"Concluir\",\n skip: \"Pular\",\n close: \"Fechar tour\",\n progress: (current, total) => `Passo ${current} de ${total}`,\n};\n\nconst EN: TourStrings = {\n next: \"Next\",\n back: \"Back\",\n finish: \"Done\",\n skip: \"Skip\",\n close: \"Close tour\",\n progress: (current, total) => `Step ${current} of ${total}`,\n};\n\nexport interface TourProps {\n /** The stops, in order. */\n steps: readonly TourStep[];\n /** Whether the tour is showing. Controlled. */\n open: boolean;\n /** Closed by `Esc`, by the close button or by \"skip\". */\n onClose: () => void;\n /** Called after the last step is confirmed. `onClose` follows it. */\n onFinish?: () => void;\n /** Current step index, when the app wants to drive it. */\n index?: number;\n /** Step changed — required only when `index` is given. */\n onIndexChange?: (index: number) => void;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Space kept clear around the highlighted element, in px. Default 4. */\n spotlightPadding?: number;\n /** Extra class on the card. */\n className?: string;\n}\n\n/**\n * A guided tour: dim the page, highlight one element at a time, explain it.\n *\n * The highlighted element stays **clickable** while everything else is blocked,\n * because the useful kind of coachmark says \"press this\" and lets you press it. That\n * is why the backdrop is four rectangles around the target rather than one overlay\n * with a `box-shadow` hole — a shadow is not hit-testable, so a hole made that way\n * would not block anything.\n *\n * Whether a user has seen the tour is the app's business: this component takes\n * `open` and emits `onClose`/`onFinish`. Persisting a flag in `localStorage` is one\n * line in the app and a wrong default here.\n *\n * @example\n * const [open, setOpen] = useState(!storage.get(\"tour-v1\"));\n *\n * <Tour\n * open={open}\n * steps={[\n * { target: \"#novo-pedido\", title: \"Comece aqui\", body: \"Todo pedido nasce deste botão.\" },\n * { target: \"[data-tour='filtros']\", body: \"E filtre por período aqui.\" },\n * ]}\n * onClose={() => setOpen(false)}\n * onFinish={() => storage.set(\"tour-v1\", true)}\n * />\n */\nexport function Tour({\n steps,\n open,\n onClose,\n onFinish,\n index,\n onIndexChange,\n locale = \"pt-BR\",\n spotlightPadding = 4,\n className,\n}: TourProps) {\n const strings = locale === \"en\" ? EN : PT_BR;\n const [internal, setInternal] = useState(0);\n const current = Math.min(index ?? internal, Math.max(0, steps.length - 1));\n const step = steps[current];\n\n const card = useRef<HTMLDivElement | null>(null);\n const [target, setTarget] = useState<TourRect | null>(null);\n const [position, setPosition] = useState<{\n placement: TourPlacement;\n top: number;\n left: number;\n }>({ placement: \"center\", top: 0, left: 0 });\n const [viewport, setViewport] = useState({ width: 0, height: 0 });\n const titleId = useId();\n const bodyId = useId();\n\n useFocusTrap(card, open);\n\n const goTo = useCallback(\n (next: number) => {\n if (index === undefined) setInternal(next);\n onIndexChange?.(next);\n },\n [index, onIndexChange],\n );\n\n /** Reset to the first step whenever the tour opens again. */\n useEffect(() => {\n if (open && index === undefined) setInternal(0);\n }, [open, index]);\n\n /**\n * Find the step's element, bring it into view, and measure everything.\n *\n * The scroll happens before measuring, and the measurement is deferred to the\n * next frame: reading a rect in the same tick as `scrollIntoView` gives the\n * position the element had *before* the scroll, which lands the card over the\n * wrong part of the page.\n */\n const measure = useCallback(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n const rect = element?.getBoundingClientRect() ?? null;\n setTarget(\n rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n );\n\n const size = {\n width: card.current?.offsetWidth ?? 320,\n height: card.current?.offsetHeight ?? 160,\n };\n const view = { width: window.innerWidth, height: window.innerHeight };\n setViewport(view);\n setPosition(\n placeCard({\n target: rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n card: size,\n viewport: view,\n preferred: step.placement,\n }),\n );\n }, [open, step]);\n\n useLayoutEffect(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n element?.scrollIntoView({ block: \"center\", inline: \"nearest\", behavior: \"smooth\" });\n const frame = requestAnimationFrame(measure);\n return () => cancelAnimationFrame(frame);\n }, [open, step, measure]);\n\n useEffect(() => {\n if (!open) return;\n window.addEventListener(\"resize\", measure);\n window.addEventListener(\"scroll\", measure, true);\n return () => {\n window.removeEventListener(\"resize\", measure);\n window.removeEventListener(\"scroll\", measure, true);\n };\n }, [open, measure]);\n\n useEffect(() => {\n if (open) card.current?.focus();\n }, [open, current]);\n\n if (!open || !step) return null;\n\n const last = current === steps.length - 1;\n\n const finish = (): void => {\n onFinish?.();\n onClose();\n };\n\n /**\n * Arrow keys walk the tour and `Esc` leaves it.\n *\n * `Esc` is handled here rather than on `window` so a tour opened over a modal\n * does not close both: the innermost handler that sees the key wins, and the\n * card has focus.\n */\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onClose();\n return;\n }\n if (event.key === \"ArrowRight\" && !last) {\n event.preventDefault();\n goTo(current + 1);\n }\n if (event.key === \"ArrowLeft\" && current > 0) {\n event.preventDefault();\n goTo(current - 1);\n }\n };\n\n return (\n <div className={styles.root} data-placement={position.placement}>\n {backdropRects(target, viewport, spotlightPadding).map((rect, i) => (\n <div\n key={i}\n className={styles.backdrop}\n style={{\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }}\n onClick={onClose}\n />\n ))}\n\n {target && (\n <div\n className={styles.spotlight}\n style={{\n top: target.top - spotlightPadding,\n left: target.left - spotlightPadding,\n width: target.width + spotlightPadding * 2,\n height: target.height + spotlightPadding * 2,\n }}\n />\n )}\n\n <div\n ref={card}\n className={cn(styles.card, className)}\n style={{ top: position.top, left: position.left }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={step.title ? titleId : undefined}\n aria-describedby={bodyId}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n <div className={styles.header}>\n <span className={styles.progress}>\n {strings.progress(current + 1, steps.length)}\n </span>\n <button\n type=\"button\"\n className={styles.close}\n aria-label={strings.close}\n onClick={onClose}\n >\n ×\n </button>\n </div>\n\n {step.title && (\n <h2 className={styles.title} id={titleId}>\n {step.title}\n </h2>\n )}\n <div className={styles.body} id={bodyId}>\n {step.body}\n </div>\n\n <div className={styles.footer}>\n {!last && (\n <button type=\"button\" className={styles.skip} onClick={onClose}>\n {strings.skip}\n </button>\n )}\n <div className={styles.actions}>\n {current > 0 && (\n <button\n type=\"button\"\n className={styles.secondary}\n onClick={() => goTo(current - 1)}\n >\n {strings.back}\n </button>\n )}\n <button\n type=\"button\"\n className={styles.primary}\n onClick={() => (last ? finish() : goTo(current + 1))}\n >\n {last ? strings.finish : strings.next}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tour.js","names":[],"sources":["../../../src/components/Tour/Tour.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — a product tour is a\n * controlled sequence (steps, index, onIndexChange, open, onClose, onFinish) drawn\n * over someone else's DOM (spotlightPadding, locale, className). The body measures\n * the target element, positions the spotlight and the card against it, and re-\n * measures on scroll and resize — one geometry, three triggers.\n */\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { cn } from \"@/utils/cn\";\n\nimport { backdropRects, placeCard, type TourPlacement, type TourRect } from \"./tour-position\";\nimport styles from \"./Tour.module.css\";\n\n/** One stop of the tour. */\nexport interface TourStep {\n /**\n * CSS selector of the element to point at.\n *\n * A selector rather than a ref, so a tour can be declared as data — in a config\n * file, from the backend, next to the copy — without every screen having to\n * thread refs up to whoever renders the tour.\n */\n target?: string;\n /** Heading of the card. */\n title?: ReactNode;\n /** The explanation. */\n body: ReactNode;\n /** Preferred side. Default `\"bottom\"`, flipped when it does not fit. */\n placement?: TourPlacement;\n}\n\n/** Labels, per locale. */\ninterface TourStrings {\n next: string;\n back: string;\n finish: string;\n skip: string;\n close: string;\n progress: (current: number, total: number) => string;\n}\n\nconst PT_BR: TourStrings = {\n next: \"Próximo\",\n back: \"Voltar\",\n finish: \"Concluir\",\n skip: \"Pular\",\n close: \"Fechar tour\",\n progress: (current, total) => `Passo ${current} de ${total}`,\n};\n\nconst EN: TourStrings = {\n next: \"Next\",\n back: \"Back\",\n finish: \"Done\",\n skip: \"Skip\",\n close: \"Close tour\",\n progress: (current, total) => `Step ${current} of ${total}`,\n};\n\nexport interface TourProps {\n /** The stops, in order. */\n steps: readonly TourStep[];\n /** Whether the tour is showing. Controlled. */\n open: boolean;\n /** Closed by `Esc`, by the close button or by \"skip\". */\n onClose: () => void;\n /** Called after the last step is confirmed. `onClose` follows it. */\n onFinish?: () => void;\n /** Current step index, when the app wants to drive it. */\n index?: number;\n /** Step changed — required only when `index` is given. */\n onIndexChange?: (index: number) => void;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Space kept clear around the highlighted element, in px. Default 4. */\n spotlightPadding?: number;\n /** Extra class on the card. */\n className?: string;\n}\n\n/**\n * A guided tour: dim the page, highlight one element at a time, explain it.\n *\n * The highlighted element stays **clickable** while everything else is blocked,\n * because the useful kind of coachmark says \"press this\" and lets you press it. That\n * is why the backdrop is four rectangles around the target rather than one overlay\n * with a `box-shadow` hole — a shadow is not hit-testable, so a hole made that way\n * would not block anything.\n *\n * Whether a user has seen the tour is the app's business: this component takes\n * `open` and emits `onClose`/`onFinish`. Persisting a flag in `localStorage` is one\n * line in the app and a wrong default here.\n *\n * @example\n * const [open, setOpen] = useState(!storage.get(\"tour-v1\"));\n *\n * <Tour\n * open={open}\n * steps={[\n * { target: \"#novo-pedido\", title: \"Comece aqui\", body: \"Todo pedido nasce deste botão.\" },\n * { target: \"[data-tour='filtros']\", body: \"E filtre por período aqui.\" },\n * ]}\n * onClose={() => setOpen(false)}\n * onFinish={() => storage.set(\"tour-v1\", true)}\n * />\n */\nexport function Tour({\n steps,\n open,\n onClose,\n onFinish,\n index,\n onIndexChange,\n locale = \"pt-BR\",\n spotlightPadding = 4,\n className,\n}: TourProps) {\n const strings = locale === \"en\" ? EN : PT_BR;\n const [internal, setInternal] = useState(0);\n const current = Math.min(index ?? internal, Math.max(0, steps.length - 1));\n const step = steps[current];\n\n const card = useRef<HTMLDivElement | null>(null);\n const [target, setTarget] = useState<TourRect | null>(null);\n const [position, setPosition] = useState<{\n placement: TourPlacement;\n top: number;\n left: number;\n }>({ placement: \"center\", top: 0, left: 0 });\n const [viewport, setViewport] = useState({ width: 0, height: 0 });\n const titleId = useId();\n const bodyId = useId();\n\n useFocusTrap(card, open);\n\n const goTo = useCallback(\n (next: number) => {\n if (index === undefined) setInternal(next);\n onIndexChange?.(next);\n },\n [index, onIndexChange],\n );\n\n /** Reset to the first step whenever the tour opens again. */\n useEffect(() => {\n if (open && index === undefined) setInternal(0);\n }, [open, index]);\n\n /**\n * Find the step's element, bring it into view, and measure everything.\n *\n * The scroll happens before measuring, and the measurement is deferred to the\n * next frame: reading a rect in the same tick as `scrollIntoView` gives the\n * position the element had *before* the scroll, which lands the card over the\n * wrong part of the page.\n */\n const measure = useCallback(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n const rect = element?.getBoundingClientRect() ?? null;\n setTarget(\n rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n );\n\n const size = {\n width: card.current?.offsetWidth ?? 320,\n height: card.current?.offsetHeight ?? 160,\n };\n const view = { width: window.innerWidth, height: window.innerHeight };\n setViewport(view);\n setPosition(\n placeCard({\n target: rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n card: size,\n viewport: view,\n preferred: step.placement,\n }),\n );\n }, [open, step]);\n\n useLayoutEffect(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n element?.scrollIntoView({ block: \"center\", inline: \"nearest\", behavior: \"smooth\" });\n const frame = requestAnimationFrame(measure);\n return () => cancelAnimationFrame(frame);\n }, [open, step, measure]);\n\n useEffect(() => {\n if (!open) return;\n window.addEventListener(\"resize\", measure);\n window.addEventListener(\"scroll\", measure, true);\n return () => {\n window.removeEventListener(\"resize\", measure);\n window.removeEventListener(\"scroll\", measure, true);\n };\n }, [open, measure]);\n\n useEffect(() => {\n if (open) card.current?.focus();\n }, [open, current]);\n\n if (!open || !step) return null;\n\n const last = current === steps.length - 1;\n\n const finish = (): void => {\n onFinish?.();\n onClose();\n };\n\n /**\n * Arrow keys walk the tour and `Esc` leaves it.\n *\n * `Esc` is handled here rather than on `window` so a tour opened over a modal\n * does not close both: the innermost handler that sees the key wins, and the\n * card has focus.\n */\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onClose();\n return;\n }\n if (event.key === \"ArrowRight\" && !last) {\n event.preventDefault();\n goTo(current + 1);\n }\n if (event.key === \"ArrowLeft\" && current > 0) {\n event.preventDefault();\n goTo(current - 1);\n }\n };\n\n return (\n <div className={styles.root} data-placement={position.placement}>\n {backdropRects(target, viewport, spotlightPadding).map((rect, i) => (\n <div\n key={i}\n className={styles.backdrop}\n style={{\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }}\n onClick={onClose}\n />\n ))}\n\n {target && (\n <div\n className={styles.spotlight}\n style={{\n top: target.top - spotlightPadding,\n left: target.left - spotlightPadding,\n width: target.width + spotlightPadding * 2,\n height: target.height + spotlightPadding * 2,\n }}\n />\n )}\n\n <div\n ref={card}\n className={cn(styles.card, className)}\n style={{ top: position.top, left: position.left }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={step.title ? titleId : undefined}\n aria-describedby={bodyId}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n <div className={styles.header}>\n <span className={styles.progress}>\n {strings.progress(current + 1, steps.length)}\n </span>\n <button\n type=\"button\"\n className={styles.close}\n aria-label={strings.close}\n onClick={onClose}\n >\n ×\n </button>\n </div>\n\n {step.title && (\n <h2 className={styles.title} id={titleId}>\n {step.title}\n </h2>\n )}\n <div className={styles.body} id={bodyId}>\n {step.body}\n </div>\n\n <div className={styles.footer}>\n {!last && (\n <button type=\"button\" className={styles.skip} onClick={onClose}>\n {strings.skip}\n </button>\n )}\n <div className={styles.actions}>\n {current > 0 && (\n <button\n type=\"button\"\n className={styles.secondary}\n onClick={() => goTo(current - 1)}\n >\n {strings.back}\n </button>\n )}\n <button\n type=\"button\"\n className={styles.primary}\n onClick={() => (last ? finish() : goTo(current + 1))}\n >\n {last ? strings.finish : strings.next}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tour.js","names":[],"sources":["../../../src/components/Tour/Tour.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — a product tour is a\n * controlled sequence (steps, index, onIndexChange, open, onClose, onFinish) drawn\n * over someone else's DOM (spotlightPadding, locale, className). The body measures\n * the target element, positions the spotlight and the card against it, and re-\n * measures on scroll and resize — one geometry, three triggers.\n */\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport { cn } from \"@/utils/cn\";\n\nimport { backdropRects, placeCard, type TourPlacement, type TourRect } from \"./tour-position\";\nimport styles from \"./Tour.module.css\";\n\n/** One stop of the tour. */\nexport interface TourStep {\n /**\n * CSS selector of the element to point at.\n *\n * A selector rather than a ref, so a tour can be declared as data — in a config\n * file, from the backend, next to the copy — without every screen having to\n * thread refs up to whoever renders the tour.\n */\n target?: string;\n /** Heading of the card. */\n title?: ReactNode;\n /** The explanation. */\n body: ReactNode;\n /** Preferred side. Default `\"bottom\"`, flipped when it does not fit. */\n placement?: TourPlacement;\n}\n\n/** Labels, per locale. */\ninterface TourStrings {\n next: string;\n back: string;\n finish: string;\n skip: string;\n close: string;\n progress: (current: number, total: number) => string;\n}\n\nconst PT_BR: TourStrings = {\n next: \"Próximo\",\n back: \"Voltar\",\n finish: \"Concluir\",\n skip: \"Pular\",\n close: \"Fechar tour\",\n progress: (current, total) => `Passo ${current} de ${total}`,\n};\n\nconst EN: TourStrings = {\n next: \"Next\",\n back: \"Back\",\n finish: \"Done\",\n skip: \"Skip\",\n close: \"Close tour\",\n progress: (current, total) => `Step ${current} of ${total}`,\n};\n\nexport interface TourProps {\n /** The stops, in order. */\n steps: readonly TourStep[];\n /** Whether the tour is showing. Controlled. */\n open: boolean;\n /** Closed by `Esc`, by the close button or by \"skip\". */\n onClose: () => void;\n /** Called after the last step is confirmed. `onClose` follows it. */\n onFinish?: () => void;\n /** Current step index, when the app wants to drive it. */\n index?: number;\n /** Step changed — required only when `index` is given. */\n onIndexChange?: (index: number) => void;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Space kept clear around the highlighted element, in px. Default 4. */\n spotlightPadding?: number;\n /** Extra class on the card. */\n className?: string;\n}\n\n/**\n * A guided tour: dim the page, highlight one element at a time, explain it.\n *\n * The highlighted element stays **clickable** while everything else is blocked,\n * because the useful kind of coachmark says \"press this\" and lets you press it. That\n * is why the backdrop is four rectangles around the target rather than one overlay\n * with a `box-shadow` hole — a shadow is not hit-testable, so a hole made that way\n * would not block anything.\n *\n * Whether a user has seen the tour is the app's business: this component takes\n * `open` and emits `onClose`/`onFinish`. Persisting a flag in `localStorage` is one\n * line in the app and a wrong default here.\n *\n * @example\n * const [open, setOpen] = useState(!storage.get(\"tour-v1\"));\n *\n * <Tour\n * open={open}\n * steps={[\n * { target: \"#novo-pedido\", title: \"Comece aqui\", body: \"Todo pedido nasce deste botão.\" },\n * { target: \"[data-tour='filtros']\", body: \"E filtre por período aqui.\" },\n * ]}\n * onClose={() => setOpen(false)}\n * onFinish={() => storage.set(\"tour-v1\", true)}\n * />\n */\nexport function Tour({\n steps,\n open,\n onClose,\n onFinish,\n index,\n onIndexChange,\n locale = \"pt-BR\",\n spotlightPadding = 4,\n className,\n}: TourProps) {\n const strings = locale === \"en\" ? EN : PT_BR;\n const [internal, setInternal] = useState(0);\n const current = Math.min(index ?? internal, Math.max(0, steps.length - 1));\n const step = steps[current];\n\n const card = useRef<HTMLDivElement | null>(null);\n const [target, setTarget] = useState<TourRect | null>(null);\n const [position, setPosition] = useState<{\n placement: TourPlacement;\n top: number;\n left: number;\n }>({ placement: \"center\", top: 0, left: 0 });\n const [viewport, setViewport] = useState({ width: 0, height: 0 });\n const titleId = useId();\n const bodyId = useId();\n\n useFocusTrap(card, open);\n\n const goTo = useCallback(\n (next: number) => {\n if (index === undefined) setInternal(next);\n onIndexChange?.(next);\n },\n [index, onIndexChange],\n );\n\n /** Reset to the first step whenever the tour opens again. */\n useEffect(() => {\n if (open && index === undefined) setInternal(0);\n }, [open, index]);\n\n /**\n * Find the step's element, bring it into view, and measure everything.\n *\n * The scroll happens before measuring, and the measurement is deferred to the\n * next frame: reading a rect in the same tick as `scrollIntoView` gives the\n * position the element had *before* the scroll, which lands the card over the\n * wrong part of the page.\n */\n const measure = useCallback(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n const rect = element?.getBoundingClientRect() ?? null;\n setTarget(\n rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n );\n\n const size = {\n width: card.current?.offsetWidth ?? 320,\n height: card.current?.offsetHeight ?? 160,\n };\n const view = { width: window.innerWidth, height: window.innerHeight };\n setViewport(view);\n setPosition(\n placeCard({\n target: rect\n ? { top: rect.top, left: rect.left, width: rect.width, height: rect.height }\n : null,\n card: size,\n viewport: view,\n preferred: step.placement,\n }),\n );\n }, [open, step]);\n\n useLayoutEffect(() => {\n if (!open || !step) return;\n const element = step.target ? document.querySelector<HTMLElement>(step.target) : null;\n element?.scrollIntoView({ block: \"center\", inline: \"nearest\", behavior: \"smooth\" });\n const frame = requestAnimationFrame(measure);\n return () => cancelAnimationFrame(frame);\n }, [open, step, measure]);\n\n useEffect(() => {\n if (!open) return;\n window.addEventListener(\"resize\", measure);\n window.addEventListener(\"scroll\", measure, true);\n return () => {\n window.removeEventListener(\"resize\", measure);\n window.removeEventListener(\"scroll\", measure, true);\n };\n }, [open, measure]);\n\n useEffect(() => {\n if (open) card.current?.focus();\n }, [open, current]);\n\n if (!open || !step) return null;\n\n const last = current === steps.length - 1;\n\n const finish = (): void => {\n onFinish?.();\n onClose();\n };\n\n /**\n * Arrow keys walk the tour and `Esc` leaves it.\n *\n * `Esc` is handled here rather than on `window` so a tour opened over a modal\n * does not close both: the innermost handler that sees the key wins, and the\n * card has focus.\n */\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Escape\") {\n event.stopPropagation();\n onClose();\n return;\n }\n if (event.key === \"ArrowRight\" && !last) {\n event.preventDefault();\n goTo(current + 1);\n }\n if (event.key === \"ArrowLeft\" && current > 0) {\n event.preventDefault();\n goTo(current - 1);\n }\n };\n\n return (\n <div className={styles.root} data-placement={position.placement}>\n {backdropRects(target, viewport, spotlightPadding).map((rect, i) => (\n <div\n key={i}\n className={styles.backdrop}\n style={{\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }}\n onClick={onClose}\n />\n ))}\n\n {target && (\n <div\n className={styles.spotlight}\n style={{\n top: target.top - spotlightPadding,\n left: target.left - spotlightPadding,\n width: target.width + spotlightPadding * 2,\n height: target.height + spotlightPadding * 2,\n }}\n />\n )}\n\n <div\n ref={card}\n className={cn(styles.card, className)}\n style={{ top: position.top, left: position.left }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={step.title ? titleId : undefined}\n aria-describedby={bodyId}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n >\n <div className={styles.header}>\n <span className={styles.progress}>\n {strings.progress(current + 1, steps.length)}\n </span>\n <button\n type=\"button\"\n className={styles.close}\n aria-label={strings.close}\n onClick={onClose}\n >\n ×\n </button>\n </div>\n\n {step.title && (\n <h2 className={styles.title} id={titleId}>\n {step.title}\n </h2>\n )}\n <div className={styles.body} id={bodyId}>\n {step.body}\n </div>\n\n <div className={styles.footer}>\n {!last && (\n <button type=\"button\" className={styles.skip} onClick={onClose}>\n {strings.skip}\n </button>\n )}\n <div className={styles.actions}>\n {current > 0 && (\n <button\n type=\"button\"\n className={styles.secondary}\n onClick={() => goTo(current - 1)}\n >\n {strings.back}\n </button>\n )}\n <button\n type=\"button\"\n className={styles.primary}\n onClick={() => (last ? finish() : goTo(current + 1))}\n >\n {last ? strings.finish : strings.next}\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Transfer.cjs","names":[],"sources":["../../../src/components/Transfer/Transfer.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — two lists that hand\n * items to each other: items and value are the model, onChange the move,\n * sourceTitle/targetTitle/renderItem/locale/height the presentation, and\n * searchable/disabled the behaviour. The body keeps both selections and the filter\n * query, and every move reads all three.\n */\nimport { useId, useMemo, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { Checkbox } from \"../Checkbox\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport {\n applyMove,\n filterItems,\n movableIds,\n splitSides,\n transferStrings,\n type TransferItem,\n type TransferSide,\n} from \"./transfer-state\";\nimport styles from \"./Transfer.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onChange\" | \"defaultValue\";\n\nexport interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The whole catalogue. Both panes are derived from it. */\n items: readonly TransferItem[];\n /** Ids on the target side. Controlled. */\n value: readonly string[];\n /** Next value, always in catalogue order. */\n onChange: (value: string[]) => void;\n /** Heading of the left pane. Default `\"Disponíveis\"`. */\n sourceTitle?: ReactNode;\n /** Heading of the right pane. Default `\"Selecionados\"`. */\n targetTitle?: ReactNode;\n /** Show a search box above each pane. Default `true` past 8 items. */\n searchable?: boolean;\n /** Custom row body. Gets the item and which side it is on. */\n renderItem?: (item: TransferItem, side: TransferSide) => ReactNode;\n /** Locale for labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Height of each pane's scroll area, any CSS length. Default `\"16rem\"`. */\n height?: string;\n /** Block every move. */\n disabled?: boolean;\n}\n\n/**\n * Two panes and a set of move controls: pick a subset out of a catalogue.\n *\n * Controlled on **ids of the target side only**. The panes are derived on every\n * render, never stored, because two stored lists drift the moment the catalogue\n * changes under them — a permission removed upstream lingers in whichever pane\n * held it, and an id in both panes is a bug nobody can see.\n *\n * @example\n * const [roles, setRoles] = useState<string[]>([]);\n *\n * <Transfer\n * items={allRoles}\n * value={roles}\n * onChange={setRoles}\n * sourceTitle=\"Papéis disponíveis\"\n * targetTitle=\"Papéis do usuário\"\n * />\n */\nexport function Transfer({\n items,\n value,\n onChange,\n sourceTitle,\n targetTitle,\n searchable,\n renderItem,\n locale = \"pt-BR\",\n height = \"16rem\",\n disabled = false,\n className,\n ...rest\n}: TransferProps) {\n const strings = transferStrings(locale);\n const baseId = useId();\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [queries, setQueries] = useState<{ source: string; target: string }>({\n source: \"\",\n target: \"\",\n });\n const [announcement, setAnnouncement] = useState(\"\");\n\n const { source, target } = useMemo(() => splitSides(items, value), [items, value]);\n const withSearch = searchable ?? items.length > 8;\n\n /**\n * What each pane is showing right now — the filter included.\n *\n * Every control acts on this, not on the pane's full contents: filtering to\n * \"sao\" and pressing \"move all\" has to move what you are looking at. Moving\n * rows the filter hid is the kind of surprise that makes people stop trusting\n * the bulk button.\n */\n const visible: Record<TransferSide, TransferItem[]> = {\n source: withSearch ? filterItems(source, queries.source) : [...source],\n target: withSearch ? filterItems(target, queries.target) : [...target],\n };\n\n const panes: Array<{ side: TransferSide; title: ReactNode; items: TransferItem[] }> = [\n { side: \"source\", title: sourceTitle ?? strings.sourceTitle, items: source },\n { side: \"target\", title: targetTitle ?? strings.targetTitle, items: target },\n ];\n\n /**\n * Move ids and drop them from the checked set.\n *\n * Clearing the checks is the point: after a move the rows are on the other\n * side, and leaving them checked means the next click on the opposite button\n * sends them straight back — which reads as the component undoing itself.\n */\n const move = (moving: readonly string[], to: TransferSide): void => {\n if (disabled || moving.length === 0) return;\n const next = applyMove({ value, moving, to, items });\n const changed = to === \"target\" ? next.length - value.length : value.length - next.length;\n onChange(next);\n setChecked((current) => {\n const remaining = new Set(current);\n for (const id of moving) remaining.delete(id);\n return remaining;\n });\n if (changed > 0) {\n const sideName = to === \"target\" ? strings.targetTitle : strings.sourceTitle;\n setAnnouncement(strings.moved(changed, String(sideName).toLowerCase()));\n }\n };\n\n const toggle = (id: string): void => {\n setChecked((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n /** Ids checked **and visible** on one side — what the move buttons act on. */\n const checkedOn = (side: TransferSide): string[] =>\n movableIds(visible[side]).filter((id) => checked.has(id));\n\n const checkedSource = checkedOn(\"source\");\n const checkedTarget = checkedOn(\"target\");\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {panes.map((pane) => {\n const shown = visible[pane.side];\n const movable = movableIds(shown);\n const checkedHere = movable.filter((id) => checked.has(id));\n const allChecked = movable.length > 0 && checkedHere.length === movable.length;\n const titleId = `${baseId}-${pane.side}-title`;\n\n return (\n <section key={pane.side} className={styles.pane} aria-labelledby={titleId}>\n <div className={styles.paneHeader}>\n <div className={styles.paneTitle}>\n <Checkbox\n checked={allChecked}\n indeterminate={checkedHere.length > 0 && !allChecked}\n disabled={disabled || movable.length === 0}\n aria-label={\n typeof pane.title === \"string\"\n ? pane.title\n : String(pane.side)\n }\n onChange={() =>\n setChecked((current) => {\n const next = new Set(current);\n for (const id of movable) {\n if (allChecked) next.delete(id);\n else next.add(id);\n }\n return next;\n })\n }\n />\n <h3 className={styles.title} id={titleId}>\n {pane.title}\n </h3>\n </div>\n <span className={styles.count}>\n {strings.selected(checkedHere.length, pane.items.length)}\n </span>\n </div>\n\n {withSearch && (\n <input\n type=\"search\"\n className={styles.search}\n value={queries[pane.side]}\n placeholder={strings.search}\n aria-label={`${strings.search}: ${\n typeof pane.title === \"string\" ? pane.title : pane.side\n }`}\n onChange={(event) =>\n setQueries((current) => ({\n ...current,\n [pane.side]: event.target.value,\n }))\n }\n />\n )}\n\n <ul className={styles.list} style={{ height }}>\n {shown.length === 0 ? (\n <li className={styles.empty}>\n {queries[pane.side] ? strings.noMatches : strings.empty}\n </li>\n ) : (\n shown.map((item) => (\n <li key={item.id} className={styles.row}>\n <Checkbox\n checked={checked.has(item.id)}\n disabled={disabled || item.disabled}\n label={\n renderItem\n ? renderItem(item, pane.side)\n : item.label\n }\n onChange={() => toggle(item.id)}\n onDoubleClick={() =>\n move(\n [item.id],\n pane.side === \"source\" ? \"target\" : \"source\",\n )\n }\n />\n </li>\n ))\n )}\n </ul>\n </section>\n );\n })}\n\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.source).length === 0}\n aria-label={strings.allToTarget}\n title={strings.allToTarget}\n onClick={() => move(movableIds(visible.source), \"target\")}\n >\n »\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedSource.length === 0}\n aria-label={strings.toTarget}\n title={strings.toTarget}\n onClick={() => move(checkedSource, \"target\")}\n >\n ›\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedTarget.length === 0}\n aria-label={strings.toSource}\n title={strings.toSource}\n onClick={() => move(checkedTarget, \"source\")}\n >\n ‹\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.target).length === 0}\n aria-label={strings.allToSource}\n title={strings.allToSource}\n onClick={() => move(movableIds(visible.target), \"source\")}\n >\n «\n </button>\n </div>\n\n <VisuallyHidden aria-live=\"polite\" role=\"status\">\n {announcement}\n </VisuallyHidden>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Transfer.cjs","names":[],"sources":["../../../src/components/Transfer/Transfer.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — two lists that hand\n * items to each other: items and value are the model, onChange the move,\n * sourceTitle/targetTitle/renderItem/locale/height the presentation, and\n * searchable/disabled the behaviour. The body keeps both selections and the filter\n * query, and every move reads all three.\n */\nimport { useId, useMemo, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { Checkbox } from \"../Checkbox\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport {\n applyMove,\n filterItems,\n movableIds,\n splitSides,\n transferStrings,\n type TransferItem,\n type TransferSide,\n} from \"./transfer-state\";\nimport styles from \"./Transfer.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onChange\" | \"defaultValue\";\n\nexport interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The whole catalogue. Both panes are derived from it. */\n items: readonly TransferItem[];\n /** Ids on the target side. Controlled. */\n value: readonly string[];\n /** Next value, always in catalogue order. */\n onChange: (value: string[]) => void;\n /** Heading of the left pane. Default `\"Disponíveis\"`. */\n sourceTitle?: ReactNode;\n /** Heading of the right pane. Default `\"Selecionados\"`. */\n targetTitle?: ReactNode;\n /** Show a search box above each pane. Default `true` past 8 items. */\n searchable?: boolean;\n /** Custom row body. Gets the item and which side it is on. */\n renderItem?: (item: TransferItem, side: TransferSide) => ReactNode;\n /** Locale for labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Height of each pane's scroll area, any CSS length. Default `\"16rem\"`. */\n height?: string;\n /** Block every move. */\n disabled?: boolean;\n}\n\n/**\n * Two panes and a set of move controls: pick a subset out of a catalogue.\n *\n * Controlled on **ids of the target side only**. The panes are derived on every\n * render, never stored, because two stored lists drift the moment the catalogue\n * changes under them — a permission removed upstream lingers in whichever pane\n * held it, and an id in both panes is a bug nobody can see.\n *\n * @example\n * const [roles, setRoles] = useState<string[]>([]);\n *\n * <Transfer\n * items={allRoles}\n * value={roles}\n * onChange={setRoles}\n * sourceTitle=\"Papéis disponíveis\"\n * targetTitle=\"Papéis do usuário\"\n * />\n */\nexport function Transfer({\n items,\n value,\n onChange,\n sourceTitle,\n targetTitle,\n searchable,\n renderItem,\n locale = \"pt-BR\",\n height = \"16rem\",\n disabled = false,\n className,\n ...rest\n}: TransferProps) {\n const strings = transferStrings(locale);\n const baseId = useId();\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [queries, setQueries] = useState<{ source: string; target: string }>({\n source: \"\",\n target: \"\",\n });\n const [announcement, setAnnouncement] = useState(\"\");\n\n const { source, target } = useMemo(() => splitSides(items, value), [items, value]);\n const withSearch = searchable ?? items.length > 8;\n\n /**\n * What each pane is showing right now — the filter included.\n *\n * Every control acts on this, not on the pane's full contents: filtering to\n * \"sao\" and pressing \"move all\" has to move what you are looking at. Moving\n * rows the filter hid is the kind of surprise that makes people stop trusting\n * the bulk button.\n */\n const visible: Record<TransferSide, TransferItem[]> = {\n source: withSearch ? filterItems(source, queries.source) : [...source],\n target: withSearch ? filterItems(target, queries.target) : [...target],\n };\n\n const panes: Array<{ side: TransferSide; title: ReactNode; items: TransferItem[] }> = [\n { side: \"source\", title: sourceTitle ?? strings.sourceTitle, items: source },\n { side: \"target\", title: targetTitle ?? strings.targetTitle, items: target },\n ];\n\n /**\n * Move ids and drop them from the checked set.\n *\n * Clearing the checks is the point: after a move the rows are on the other\n * side, and leaving them checked means the next click on the opposite button\n * sends them straight back — which reads as the component undoing itself.\n */\n const move = (moving: readonly string[], to: TransferSide): void => {\n if (disabled || moving.length === 0) return;\n const next = applyMove({ value, moving, to, items });\n const changed = to === \"target\" ? next.length - value.length : value.length - next.length;\n onChange(next);\n setChecked((current) => {\n const remaining = new Set(current);\n for (const id of moving) remaining.delete(id);\n return remaining;\n });\n if (changed > 0) {\n const sideName = to === \"target\" ? strings.targetTitle : strings.sourceTitle;\n setAnnouncement(strings.moved(changed, String(sideName).toLowerCase()));\n }\n };\n\n const toggle = (id: string): void => {\n setChecked((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n /** Ids checked **and visible** on one side — what the move buttons act on. */\n const checkedOn = (side: TransferSide): string[] =>\n movableIds(visible[side]).filter((id) => checked.has(id));\n\n const checkedSource = checkedOn(\"source\");\n const checkedTarget = checkedOn(\"target\");\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {panes.map((pane) => {\n const shown = visible[pane.side];\n const movable = movableIds(shown);\n const checkedHere = movable.filter((id) => checked.has(id));\n const allChecked = movable.length > 0 && checkedHere.length === movable.length;\n const titleId = `${baseId}-${pane.side}-title`;\n\n return (\n <section key={pane.side} className={styles.pane} aria-labelledby={titleId}>\n <div className={styles.paneHeader}>\n <div className={styles.paneTitle}>\n <Checkbox\n checked={allChecked}\n indeterminate={checkedHere.length > 0 && !allChecked}\n disabled={disabled || movable.length === 0}\n aria-label={\n typeof pane.title === \"string\"\n ? pane.title\n : String(pane.side)\n }\n onChange={() =>\n setChecked((current) => {\n const next = new Set(current);\n for (const id of movable) {\n if (allChecked) next.delete(id);\n else next.add(id);\n }\n return next;\n })\n }\n />\n <h3 className={styles.title} id={titleId}>\n {pane.title}\n </h3>\n </div>\n <span className={styles.count}>\n {strings.selected(checkedHere.length, pane.items.length)}\n </span>\n </div>\n\n {withSearch && (\n <input\n type=\"search\"\n className={styles.search}\n value={queries[pane.side]}\n placeholder={strings.search}\n aria-label={`${strings.search}: ${\n typeof pane.title === \"string\" ? pane.title : pane.side\n }`}\n onChange={(event) =>\n setQueries((current) => ({\n ...current,\n [pane.side]: event.target.value,\n }))\n }\n />\n )}\n\n <ul className={styles.list} style={{ height }}>\n {shown.length === 0 ? (\n <li className={styles.empty}>\n {queries[pane.side] ? strings.noMatches : strings.empty}\n </li>\n ) : (\n shown.map((item) => (\n <li key={item.id} className={styles.row}>\n <Checkbox\n checked={checked.has(item.id)}\n disabled={disabled || item.disabled}\n label={\n renderItem\n ? renderItem(item, pane.side)\n : item.label\n }\n onChange={() => toggle(item.id)}\n onDoubleClick={() =>\n move(\n [item.id],\n pane.side === \"source\" ? \"target\" : \"source\",\n )\n }\n />\n </li>\n ))\n )}\n </ul>\n </section>\n );\n })}\n\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.source).length === 0}\n aria-label={strings.allToTarget}\n title={strings.allToTarget}\n onClick={() => move(movableIds(visible.source), \"target\")}\n >\n »\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedSource.length === 0}\n aria-label={strings.toTarget}\n title={strings.toTarget}\n onClick={() => move(checkedSource, \"target\")}\n >\n ›\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedTarget.length === 0}\n aria-label={strings.toSource}\n title={strings.toSource}\n onClick={() => move(checkedTarget, \"source\")}\n >\n ‹\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.target).length === 0}\n aria-label={strings.allToSource}\n title={strings.allToSource}\n onClick={() => move(movableIds(visible.target), \"source\")}\n >\n «\n </button>\n </div>\n\n <VisuallyHidden aria-live=\"polite\" role=\"status\">\n {announcement}\n </VisuallyHidden>\n </div>\n );\n}\n"],"mappings":"yPAqEA,SAAgB,EAAS,CACrB,QACA,QACA,WACA,cACA,cACA,aACA,aACA,SAAS,QACT,SAAS,QACT,WAAW,GACX,YACA,GAAG,GACW,CACd,IAAM,EAAU,EAAA,gBAAgB,CAAM,EAChC,GAAA,EAAS,EAAA,MAAA,CAAM,EACf,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAsB,IAAI,GAAK,EACvD,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAA6C,CACvE,OAAQ,GACR,OAAQ,EACZ,CAAC,EACK,CAAC,EAAc,IAAA,EAAmB,EAAA,SAAA,CAAS,EAAE,EAE7C,CAAE,SAAQ,WAAA,EAAW,EAAA,QAAA,KAAc,EAAA,WAAW,EAAO,CAAK,EAAG,CAAC,EAAO,CAAK,CAAC,EAC3E,EAAa,GAAc,EAAM,OAAS,EAU1C,EAAgD,CAClD,OAAQ,EAAa,EAAA,YAAY,EAAQ,EAAQ,MAAM,EAAI,CAAC,GAAG,CAAM,EACrE,OAAQ,EAAa,EAAA,YAAY,EAAQ,EAAQ,MAAM,EAAI,CAAC,GAAG,CAAM,CACzE,EAEM,EAAgF,CAClF,CAAE,KAAM,SAAU,MAAO,GAAe,EAAQ,YAAa,MAAO,CAAO,EAC3E,CAAE,KAAM,SAAU,MAAO,GAAe,EAAQ,YAAa,MAAO,CAAO,CAC/E,EASM,GAAQ,EAA2B,IAA2B,CAChE,GAAI,GAAY,EAAO,SAAW,EAAG,OACrC,IAAM,EAAO,EAAA,UAAU,CAAE,QAAO,SAAQ,KAAI,OAAM,CAAC,EAC7C,EAAU,IAAO,SAAW,EAAK,OAAS,EAAM,OAAS,EAAM,OAAS,EAAK,OAOnF,GANA,EAAS,CAAI,EACb,EAAY,GAAY,CACpB,IAAM,EAAY,IAAI,IAAI,CAAO,EACjC,IAAK,IAAM,KAAM,EAAQ,EAAU,OAAO,CAAE,EAC5C,OAAO,CACX,CAAC,EACG,EAAU,EAAG,CACb,IAAM,EAAW,IAAO,SAAW,EAAQ,YAAc,EAAQ,YACjE,EAAgB,EAAQ,MAAM,EAAS,OAAO,CAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAC1E,CACJ,EAEM,EAAU,GAAqB,CACjC,EAAY,GAAY,CACpB,IAAM,EAAO,IAAI,IAAI,CAAO,EAG5B,OAFI,EAAK,IAAI,CAAE,EAAG,EAAK,OAAO,CAAE,EAC3B,EAAK,IAAI,CAAE,EACT,CACX,CAAC,CACL,EAGM,EAAa,GACf,EAAA,WAAW,EAAQ,EAAK,CAAC,CAAC,OAAQ,GAAO,EAAQ,IAAI,CAAE,CAAC,EAEtD,EAAgB,EAAU,QAAQ,EAClC,EAAgB,EAAU,QAAQ,EAExC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAAG,GAAI,EAAnD,SAAA,CACK,EAAM,IAAK,GAAS,CACjB,IAAM,EAAQ,EAAQ,EAAK,MACrB,EAAU,EAAA,WAAW,CAAK,EAC1B,EAAc,EAAQ,OAAQ,GAAO,EAAQ,IAAI,CAAE,CAAC,EACpD,EAAa,EAAQ,OAAS,GAAK,EAAY,SAAW,EAAQ,OAClE,EAAU,GAAG,EAAO,GAAG,EAAK,KAAK,QAEvC,OACI,EAAA,EAAA,KAAA,CAAC,UAAD,CAAyB,UAAW,EAAA,QAAO,KAAM,kBAAiB,EAAlE,SAAA,EACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,WAAvB,SAAA,EACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,UAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CACI,QAAS,EACT,cAAe,EAAY,OAAS,GAAK,CAAC,EAC1C,SAAU,GAAY,EAAQ,SAAW,EACzC,aACI,OAAO,EAAK,OAAU,SAChB,EAAK,MACL,OAAO,EAAK,IAAI,EAE1B,aACI,EAAY,GAAY,CACpB,IAAM,EAAO,IAAI,IAAI,CAAO,EAC5B,IAAK,IAAM,KAAM,EACT,EAAY,EAAK,OAAO,CAAE,EACzB,EAAK,IAAI,CAAE,EAEpB,OAAO,CACX,CAAC,CAER,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAO,GAAI,EAC5B,SAAA,EAAK,KACN,CAAA,CACH,CACL,CAAA,GAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,MACnB,SAAA,EAAQ,SAAS,EAAY,OAAQ,EAAK,MAAM,MAAM,CACrD,CAAA,CACL,IAEJ,IACG,EAAA,EAAA,IAAA,CAAC,QAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,OAClB,MAAO,EAAQ,EAAK,MACpB,YAAa,EAAQ,OACrB,aAAY,GAAG,EAAQ,OAAO,IAC1B,OAAO,EAAK,OAAU,SAAW,EAAK,MAAQ,EAAK,OAEvD,SAAW,GACP,EAAY,IAAa,CACrB,GAAG,GACF,EAAK,MAAO,EAAM,OAAO,KAC9B,EAAE,CAET,CAAA,GAGL,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,KAAM,MAAO,CAAE,QAAO,EACvC,SAAA,EAAM,SAAW,GACd,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MACjB,SAAA,EAAQ,EAAK,MAAQ,EAAQ,UAAY,EAAQ,KAClD,CAAA,EAEJ,EAAM,IAAK,IACP,EAAA,EAAA,IAAA,CAAC,KAAD,CAAkB,UAAW,EAAA,QAAO,IAChC,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,SAAD,CACI,QAAS,EAAQ,IAAI,EAAK,EAAE,EAC5B,SAAU,GAAY,EAAK,SAC3B,MACI,EACM,EAAW,EAAM,EAAK,IAAI,EAC1B,EAAK,MAEf,aAAgB,EAAO,EAAK,EAAE,EAC9B,kBACI,EACI,CAAC,EAAK,EAAE,EACR,EAAK,OAAS,SAAW,SAAW,QACxC,CAEP,CAAA,CACD,EAjBK,EAAK,EAiBV,CACP,CAEL,CAAA,CACC,CA9EK,EAAA,EAAK,IA8EV,CAEjB,CAAC,GAED,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,SAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,SAAU,GAAY,EAAA,WAAW,EAAQ,MAAM,CAAC,CAAC,SAAW,EAC5D,aAAY,EAAQ,YACpB,MAAO,EAAQ,YACf,YAAe,EAAK,EAAA,WAAW,EAAQ,MAAM,EAAG,QAAQ,EAC3D,SAAA,GAEO,CAAA,GACR,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,SAAU,GAAY,EAAc,SAAW,EAC/C,aAAY,EAAQ,SACpB,MAAO,EAAQ,SACf,YAAe,EAAK,EAAe,QAAQ,EAC9C,SAAA,GAEO,CAAA,GACR,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,SAAU,GAAY,EAAc,SAAW,EAC/C,aAAY,EAAQ,SACpB,MAAO,EAAQ,SACf,YAAe,EAAK,EAAe,QAAQ,EAC9C,SAAA,GAEO,CAAA,GACR,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,QAClB,SAAU,GAAY,EAAA,WAAW,EAAQ,MAAM,CAAC,CAAC,SAAW,EAC5D,aAAY,EAAQ,YACpB,MAAO,EAAQ,YACf,YAAe,EAAK,EAAA,WAAW,EAAQ,MAAM,EAAG,QAAQ,EAC3D,SAAA,GAEO,CAAA,CACP,KAEL,EAAA,EAAA,IAAA,CAAC,EAAA,eAAD,CAAgB,YAAU,SAAS,KAAK,SACnC,SAAA,CACW,CAAA,CACf,GAEb"}
@@ -1 +1 @@
1
- {"version":3,"file":"Transfer.js","names":[],"sources":["../../../src/components/Transfer/Transfer.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — two lists that hand\n * items to each other: items and value are the model, onChange the move,\n * sourceTitle/targetTitle/renderItem/locale/height the presentation, and\n * searchable/disabled the behaviour. The body keeps both selections and the filter\n * query, and every move reads all three.\n */\nimport { useId, useMemo, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { Checkbox } from \"../Checkbox\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport {\n applyMove,\n filterItems,\n movableIds,\n splitSides,\n transferStrings,\n type TransferItem,\n type TransferSide,\n} from \"./transfer-state\";\nimport styles from \"./Transfer.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onChange\" | \"defaultValue\";\n\nexport interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The whole catalogue. Both panes are derived from it. */\n items: readonly TransferItem[];\n /** Ids on the target side. Controlled. */\n value: readonly string[];\n /** Next value, always in catalogue order. */\n onChange: (value: string[]) => void;\n /** Heading of the left pane. Default `\"Disponíveis\"`. */\n sourceTitle?: ReactNode;\n /** Heading of the right pane. Default `\"Selecionados\"`. */\n targetTitle?: ReactNode;\n /** Show a search box above each pane. Default `true` past 8 items. */\n searchable?: boolean;\n /** Custom row body. Gets the item and which side it is on. */\n renderItem?: (item: TransferItem, side: TransferSide) => ReactNode;\n /** Locale for labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Height of each pane's scroll area, any CSS length. Default `\"16rem\"`. */\n height?: string;\n /** Block every move. */\n disabled?: boolean;\n}\n\n/**\n * Two panes and a set of move controls: pick a subset out of a catalogue.\n *\n * Controlled on **ids of the target side only**. The panes are derived on every\n * render, never stored, because two stored lists drift the moment the catalogue\n * changes under them — a permission removed upstream lingers in whichever pane\n * held it, and an id in both panes is a bug nobody can see.\n *\n * @example\n * const [roles, setRoles] = useState<string[]>([]);\n *\n * <Transfer\n * items={allRoles}\n * value={roles}\n * onChange={setRoles}\n * sourceTitle=\"Papéis disponíveis\"\n * targetTitle=\"Papéis do usuário\"\n * />\n */\nexport function Transfer({\n items,\n value,\n onChange,\n sourceTitle,\n targetTitle,\n searchable,\n renderItem,\n locale = \"pt-BR\",\n height = \"16rem\",\n disabled = false,\n className,\n ...rest\n}: TransferProps) {\n const strings = transferStrings(locale);\n const baseId = useId();\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [queries, setQueries] = useState<{ source: string; target: string }>({\n source: \"\",\n target: \"\",\n });\n const [announcement, setAnnouncement] = useState(\"\");\n\n const { source, target } = useMemo(() => splitSides(items, value), [items, value]);\n const withSearch = searchable ?? items.length > 8;\n\n /**\n * What each pane is showing right now — the filter included.\n *\n * Every control acts on this, not on the pane's full contents: filtering to\n * \"sao\" and pressing \"move all\" has to move what you are looking at. Moving\n * rows the filter hid is the kind of surprise that makes people stop trusting\n * the bulk button.\n */\n const visible: Record<TransferSide, TransferItem[]> = {\n source: withSearch ? filterItems(source, queries.source) : [...source],\n target: withSearch ? filterItems(target, queries.target) : [...target],\n };\n\n const panes: Array<{ side: TransferSide; title: ReactNode; items: TransferItem[] }> = [\n { side: \"source\", title: sourceTitle ?? strings.sourceTitle, items: source },\n { side: \"target\", title: targetTitle ?? strings.targetTitle, items: target },\n ];\n\n /**\n * Move ids and drop them from the checked set.\n *\n * Clearing the checks is the point: after a move the rows are on the other\n * side, and leaving them checked means the next click on the opposite button\n * sends them straight back — which reads as the component undoing itself.\n */\n const move = (moving: readonly string[], to: TransferSide): void => {\n if (disabled || moving.length === 0) return;\n const next = applyMove({ value, moving, to, items });\n const changed = to === \"target\" ? next.length - value.length : value.length - next.length;\n onChange(next);\n setChecked((current) => {\n const remaining = new Set(current);\n for (const id of moving) remaining.delete(id);\n return remaining;\n });\n if (changed > 0) {\n const sideName = to === \"target\" ? strings.targetTitle : strings.sourceTitle;\n setAnnouncement(strings.moved(changed, String(sideName).toLowerCase()));\n }\n };\n\n const toggle = (id: string): void => {\n setChecked((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n /** Ids checked **and visible** on one side — what the move buttons act on. */\n const checkedOn = (side: TransferSide): string[] =>\n movableIds(visible[side]).filter((id) => checked.has(id));\n\n const checkedSource = checkedOn(\"source\");\n const checkedTarget = checkedOn(\"target\");\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {panes.map((pane) => {\n const shown = visible[pane.side];\n const movable = movableIds(shown);\n const checkedHere = movable.filter((id) => checked.has(id));\n const allChecked = movable.length > 0 && checkedHere.length === movable.length;\n const titleId = `${baseId}-${pane.side}-title`;\n\n return (\n <section key={pane.side} className={styles.pane} aria-labelledby={titleId}>\n <div className={styles.paneHeader}>\n <div className={styles.paneTitle}>\n <Checkbox\n checked={allChecked}\n indeterminate={checkedHere.length > 0 && !allChecked}\n disabled={disabled || movable.length === 0}\n aria-label={\n typeof pane.title === \"string\"\n ? pane.title\n : String(pane.side)\n }\n onChange={() =>\n setChecked((current) => {\n const next = new Set(current);\n for (const id of movable) {\n if (allChecked) next.delete(id);\n else next.add(id);\n }\n return next;\n })\n }\n />\n <h3 className={styles.title} id={titleId}>\n {pane.title}\n </h3>\n </div>\n <span className={styles.count}>\n {strings.selected(checkedHere.length, pane.items.length)}\n </span>\n </div>\n\n {withSearch && (\n <input\n type=\"search\"\n className={styles.search}\n value={queries[pane.side]}\n placeholder={strings.search}\n aria-label={`${strings.search}: ${\n typeof pane.title === \"string\" ? pane.title : pane.side\n }`}\n onChange={(event) =>\n setQueries((current) => ({\n ...current,\n [pane.side]: event.target.value,\n }))\n }\n />\n )}\n\n <ul className={styles.list} style={{ height }}>\n {shown.length === 0 ? (\n <li className={styles.empty}>\n {queries[pane.side] ? strings.noMatches : strings.empty}\n </li>\n ) : (\n shown.map((item) => (\n <li key={item.id} className={styles.row}>\n <Checkbox\n checked={checked.has(item.id)}\n disabled={disabled || item.disabled}\n label={\n renderItem\n ? renderItem(item, pane.side)\n : item.label\n }\n onChange={() => toggle(item.id)}\n onDoubleClick={() =>\n move(\n [item.id],\n pane.side === \"source\" ? \"target\" : \"source\",\n )\n }\n />\n </li>\n ))\n )}\n </ul>\n </section>\n );\n })}\n\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.source).length === 0}\n aria-label={strings.allToTarget}\n title={strings.allToTarget}\n onClick={() => move(movableIds(visible.source), \"target\")}\n >\n »\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedSource.length === 0}\n aria-label={strings.toTarget}\n title={strings.toTarget}\n onClick={() => move(checkedSource, \"target\")}\n >\n ›\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedTarget.length === 0}\n aria-label={strings.toSource}\n title={strings.toSource}\n onClick={() => move(checkedTarget, \"source\")}\n >\n ‹\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.target).length === 0}\n aria-label={strings.allToSource}\n title={strings.allToSource}\n onClick={() => move(movableIds(visible.target), \"source\")}\n >\n «\n </button>\n </div>\n\n <VisuallyHidden aria-live=\"polite\" role=\"status\">\n {announcement}\n </VisuallyHidden>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Transfer.js","names":[],"sources":["../../../src/components/Transfer/Transfer.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — two lists that hand\n * items to each other: items and value are the model, onChange the move,\n * sourceTitle/targetTitle/renderItem/locale/height the presentation, and\n * searchable/disabled the behaviour. The body keeps both selections and the filter\n * query, and every move reads all three.\n */\nimport { useId, useMemo, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { Checkbox } from \"../Checkbox\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport {\n applyMove,\n filterItems,\n movableIds,\n splitSides,\n transferStrings,\n type TransferItem,\n type TransferSide,\n} from \"./transfer-state\";\nimport styles from \"./Transfer.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onChange\" | \"defaultValue\";\n\nexport interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n /** The whole catalogue. Both panes are derived from it. */\n items: readonly TransferItem[];\n /** Ids on the target side. Controlled. */\n value: readonly string[];\n /** Next value, always in catalogue order. */\n onChange: (value: string[]) => void;\n /** Heading of the left pane. Default `\"Disponíveis\"`. */\n sourceTitle?: ReactNode;\n /** Heading of the right pane. Default `\"Selecionados\"`. */\n targetTitle?: ReactNode;\n /** Show a search box above each pane. Default `true` past 8 items. */\n searchable?: boolean;\n /** Custom row body. Gets the item and which side it is on. */\n renderItem?: (item: TransferItem, side: TransferSide) => ReactNode;\n /** Locale for labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Height of each pane's scroll area, any CSS length. Default `\"16rem\"`. */\n height?: string;\n /** Block every move. */\n disabled?: boolean;\n}\n\n/**\n * Two panes and a set of move controls: pick a subset out of a catalogue.\n *\n * Controlled on **ids of the target side only**. The panes are derived on every\n * render, never stored, because two stored lists drift the moment the catalogue\n * changes under them — a permission removed upstream lingers in whichever pane\n * held it, and an id in both panes is a bug nobody can see.\n *\n * @example\n * const [roles, setRoles] = useState<string[]>([]);\n *\n * <Transfer\n * items={allRoles}\n * value={roles}\n * onChange={setRoles}\n * sourceTitle=\"Papéis disponíveis\"\n * targetTitle=\"Papéis do usuário\"\n * />\n */\nexport function Transfer({\n items,\n value,\n onChange,\n sourceTitle,\n targetTitle,\n searchable,\n renderItem,\n locale = \"pt-BR\",\n height = \"16rem\",\n disabled = false,\n className,\n ...rest\n}: TransferProps) {\n const strings = transferStrings(locale);\n const baseId = useId();\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [queries, setQueries] = useState<{ source: string; target: string }>({\n source: \"\",\n target: \"\",\n });\n const [announcement, setAnnouncement] = useState(\"\");\n\n const { source, target } = useMemo(() => splitSides(items, value), [items, value]);\n const withSearch = searchable ?? items.length > 8;\n\n /**\n * What each pane is showing right now — the filter included.\n *\n * Every control acts on this, not on the pane's full contents: filtering to\n * \"sao\" and pressing \"move all\" has to move what you are looking at. Moving\n * rows the filter hid is the kind of surprise that makes people stop trusting\n * the bulk button.\n */\n const visible: Record<TransferSide, TransferItem[]> = {\n source: withSearch ? filterItems(source, queries.source) : [...source],\n target: withSearch ? filterItems(target, queries.target) : [...target],\n };\n\n const panes: Array<{ side: TransferSide; title: ReactNode; items: TransferItem[] }> = [\n { side: \"source\", title: sourceTitle ?? strings.sourceTitle, items: source },\n { side: \"target\", title: targetTitle ?? strings.targetTitle, items: target },\n ];\n\n /**\n * Move ids and drop them from the checked set.\n *\n * Clearing the checks is the point: after a move the rows are on the other\n * side, and leaving them checked means the next click on the opposite button\n * sends them straight back — which reads as the component undoing itself.\n */\n const move = (moving: readonly string[], to: TransferSide): void => {\n if (disabled || moving.length === 0) return;\n const next = applyMove({ value, moving, to, items });\n const changed = to === \"target\" ? next.length - value.length : value.length - next.length;\n onChange(next);\n setChecked((current) => {\n const remaining = new Set(current);\n for (const id of moving) remaining.delete(id);\n return remaining;\n });\n if (changed > 0) {\n const sideName = to === \"target\" ? strings.targetTitle : strings.sourceTitle;\n setAnnouncement(strings.moved(changed, String(sideName).toLowerCase()));\n }\n };\n\n const toggle = (id: string): void => {\n setChecked((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n /** Ids checked **and visible** on one side — what the move buttons act on. */\n const checkedOn = (side: TransferSide): string[] =>\n movableIds(visible[side]).filter((id) => checked.has(id));\n\n const checkedSource = checkedOn(\"source\");\n const checkedTarget = checkedOn(\"target\");\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n {panes.map((pane) => {\n const shown = visible[pane.side];\n const movable = movableIds(shown);\n const checkedHere = movable.filter((id) => checked.has(id));\n const allChecked = movable.length > 0 && checkedHere.length === movable.length;\n const titleId = `${baseId}-${pane.side}-title`;\n\n return (\n <section key={pane.side} className={styles.pane} aria-labelledby={titleId}>\n <div className={styles.paneHeader}>\n <div className={styles.paneTitle}>\n <Checkbox\n checked={allChecked}\n indeterminate={checkedHere.length > 0 && !allChecked}\n disabled={disabled || movable.length === 0}\n aria-label={\n typeof pane.title === \"string\"\n ? pane.title\n : String(pane.side)\n }\n onChange={() =>\n setChecked((current) => {\n const next = new Set(current);\n for (const id of movable) {\n if (allChecked) next.delete(id);\n else next.add(id);\n }\n return next;\n })\n }\n />\n <h3 className={styles.title} id={titleId}>\n {pane.title}\n </h3>\n </div>\n <span className={styles.count}>\n {strings.selected(checkedHere.length, pane.items.length)}\n </span>\n </div>\n\n {withSearch && (\n <input\n type=\"search\"\n className={styles.search}\n value={queries[pane.side]}\n placeholder={strings.search}\n aria-label={`${strings.search}: ${\n typeof pane.title === \"string\" ? pane.title : pane.side\n }`}\n onChange={(event) =>\n setQueries((current) => ({\n ...current,\n [pane.side]: event.target.value,\n }))\n }\n />\n )}\n\n <ul className={styles.list} style={{ height }}>\n {shown.length === 0 ? (\n <li className={styles.empty}>\n {queries[pane.side] ? strings.noMatches : strings.empty}\n </li>\n ) : (\n shown.map((item) => (\n <li key={item.id} className={styles.row}>\n <Checkbox\n checked={checked.has(item.id)}\n disabled={disabled || item.disabled}\n label={\n renderItem\n ? renderItem(item, pane.side)\n : item.label\n }\n onChange={() => toggle(item.id)}\n onDoubleClick={() =>\n move(\n [item.id],\n pane.side === \"source\" ? \"target\" : \"source\",\n )\n }\n />\n </li>\n ))\n )}\n </ul>\n </section>\n );\n })}\n\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.source).length === 0}\n aria-label={strings.allToTarget}\n title={strings.allToTarget}\n onClick={() => move(movableIds(visible.source), \"target\")}\n >\n »\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedSource.length === 0}\n aria-label={strings.toTarget}\n title={strings.toTarget}\n onClick={() => move(checkedSource, \"target\")}\n >\n ›\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || checkedTarget.length === 0}\n aria-label={strings.toSource}\n title={strings.toSource}\n onClick={() => move(checkedTarget, \"source\")}\n >\n ‹\n </button>\n <button\n type=\"button\"\n className={styles.control}\n disabled={disabled || movableIds(visible.target).length === 0}\n aria-label={strings.allToSource}\n title={strings.allToSource}\n onClick={() => move(movableIds(visible.target), \"source\")}\n >\n «\n </button>\n </div>\n\n <VisuallyHidden aria-live=\"polite\" role=\"status\">\n {announcement}\n </VisuallyHidden>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./TreeView.module.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("lucide-react");function a(e,t,n=0,r=null,i=[]){for(let o of e){let e=Array.isArray(o.children),s=e&&t.has(o.id);i.push({node:o,depth:n,parentId:r,hasChildren:e,expanded:s}),s&&o.children&&a(o.children,t,n+1,o.id,i)}return i}function o({nodes:o,expandedIds:s,defaultExpandedIds:c=[],onExpandedChange:l,selectedId:u,defaultSelectedId:d=null,onSelect:f,toggleOnSelect:p=!0,label:m,className:h}){let g=s!==void 0,[_,v]=(0,n.useState)(c),y=g?s:_,b=u!==void 0,[x,S]=(0,n.useState)(d),C=b?u:x,w=(0,n.useMemo)(()=>new Set(y),[y]),T=(0,n.useMemo)(()=>a(o,w),[o,w]),[E,D]=(0,n.useState)(null),O=(0,n.useRef)(null),k=E??C??T.find(e=>!e.node.disabled)?.node.id??null,A=(0,n.useCallback)(e=>{g||v(e),l?.(e)},[g,l]),j=(0,n.useCallback)(e=>{A(y.includes(e)?y.filter(t=>t!==e):[...y,e])},[y,A]),M=(0,n.useCallback)(e=>{e.node.disabled||(b||S(e.node.id),D(e.node.id),f?.(e.node),e.hasChildren&&p&&j(e.node.id))},[f,b,j,p]),N=(0,n.useCallback)(e=>{D(e),(O.current?.querySelector(`[data-tree-id="${e}"]`))?.focus()},[]),P=(0,n.useCallback)((e,t)=>{for(let n=e+t;n>=0&&n<T.length;n+=t)if(!T[n].node.disabled){N(T[n].node.id);return}},[T,N]),F=(0,n.useCallback)((e,t,n)=>{switch(e.key){case`ArrowDown`:e.preventDefault(),P(n,1);break;case`ArrowUp`:e.preventDefault(),P(n,-1);break;case`ArrowRight`:e.preventDefault(),t.hasChildren&&!t.expanded?j(t.node.id):t.expanded&&P(n,1);break;case`ArrowLeft`:e.preventDefault(),t.expanded?j(t.node.id):t.parentId&&N(t.parentId);break;case`Home`:e.preventDefault(),P(-1,1);break;case`End`:e.preventDefault(),P(T.length,-1);break;case`Enter`:case` `:e.preventDefault(),M(t);break;default:break}},[P,T.length,M,j,N]);return(0,r.jsx)(`ul`,{ref:O,role:`tree`,"aria-label":m,className:e.cn(t.default.tree,h),children:T.map((n,a)=>{let o=C===n.node.id;return(0,r.jsx)(`li`,{role:`treeitem`,"aria-expanded":n.hasChildren?n.expanded:void 0,"aria-selected":o,"aria-level":n.depth+1,"aria-disabled":n.node.disabled||void 0,className:t.default.item,children:(0,r.jsxs)(`div`,{"data-tree-id":n.node.id,className:e.cn(t.default.row,o&&t.default.selected,n.node.disabled&&t.default.disabled),style:{paddingInlineStart:`calc(${n.depth} * var(--tempest-space-5))`},tabIndex:k===n.node.id&&!n.node.disabled?0:-1,onClick:()=>M(n),onFocus:()=>D(n.node.id),onKeyDown:e=>F(e,n,a),children:[n.hasChildren?(0,r.jsx)(`span`,{className:e.cn(t.default.chevron,n.expanded&&t.default.chevronOpen),"aria-hidden":`true`,onClick:e=>{e.stopPropagation(),n.node.disabled||j(n.node.id)},children:(0,r.jsx)(i.ChevronRight,{size:14})}):(0,r.jsx)(`span`,{className:t.default.chevronPlaceholder,"aria-hidden":`true`}),n.node.icon?(0,r.jsx)(`span`,{className:t.default.icon,"aria-hidden":`true`,children:n.node.icon}):null,(0,r.jsx)(`span`,{className:t.default.label,children:n.node.label})]})},n.node.id)})})}exports.TreeView=o;
1
+ const e=require("../../utils/cn.cjs"),t=require("./TreeView.module.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("lucide-react");function a(e,t,n=0,r=null,i=[]){for(let o of e){let e=Array.isArray(o.children),s=e&&t.has(o.id);i.push({node:o,depth:n,parentId:r,hasChildren:e,expanded:s}),s&&o.children&&a(o.children,t,n+1,o.id,i)}return i}function o({nodes:o,expandedIds:s,defaultExpandedIds:c=[],onExpandedChange:l,selectedId:u,defaultSelectedId:d=null,onSelect:f,toggleOnSelect:p=!0,label:m,className:h}){let g=s!==void 0,[_,v]=(0,n.useState)(c),y=g?s:_,b=u!==void 0,[x,S]=(0,n.useState)(d),C=b?u:x,w=(0,n.useMemo)(()=>new Set(y),[y]),T=(0,n.useMemo)(()=>a(o,w),[o,w]),[E,D]=(0,n.useState)(null),O=(0,n.useRef)(null),k=E??C??T.find(e=>!e.node.disabled)?.node.id??null,A=(0,n.useCallback)(e=>{g||v(e),l?.(e)},[g,l]),j=(0,n.useCallback)(e=>{A(y.includes(e)?y.filter(t=>t!==e):[...y,e])},[y,A]),M=(0,n.useCallback)(e=>{e.node.disabled||(b||S(e.node.id),D(e.node.id),f?.(e.node),e.hasChildren&&p&&j(e.node.id))},[f,b,j,p]),N=(0,n.useCallback)(e=>{D(e),(O.current?.querySelector(`[data-tree-id="${e}"]`))?.focus()},[]),P=(0,n.useCallback)((e,t)=>{for(let n=e+t;n>=0&&n<T.length;n+=t)if(!T[n].node.disabled){N(T[n].node.id);return}},[T,N]),F=(0,n.useCallback)((e,t,n)=>{switch(e.key){case`ArrowDown`:e.preventDefault(),P(n,1);break;case`ArrowUp`:e.preventDefault(),P(n,-1);break;case`ArrowRight`:e.preventDefault(),t.hasChildren&&!t.expanded?j(t.node.id):t.expanded&&P(n,1);break;case`ArrowLeft`:e.preventDefault(),t.expanded?j(t.node.id):t.parentId&&N(t.parentId);break;case`Home`:e.preventDefault(),P(-1,1);break;case`End`:e.preventDefault(),P(T.length,-1);break;case`Enter`:case` `:e.preventDefault(),M(t)}},[P,T.length,M,j,N]);return(0,r.jsx)(`ul`,{ref:O,role:`tree`,"aria-label":m,className:e.cn(t.default.tree,h),children:T.map((n,a)=>{let o=C===n.node.id;return(0,r.jsx)(`li`,{role:`treeitem`,"aria-expanded":n.hasChildren?n.expanded:void 0,"aria-selected":o,"aria-level":n.depth+1,"aria-disabled":n.node.disabled||void 0,className:t.default.item,children:(0,r.jsxs)(`div`,{"data-tree-id":n.node.id,className:e.cn(t.default.row,o&&t.default.selected,n.node.disabled&&t.default.disabled),style:{paddingInlineStart:`calc(${n.depth} * var(--tempest-space-5))`},tabIndex:k===n.node.id&&!n.node.disabled?0:-1,onClick:()=>M(n),onFocus:()=>D(n.node.id),onKeyDown:e=>F(e,n,a),children:[n.hasChildren?(0,r.jsx)(`span`,{className:e.cn(t.default.chevron,n.expanded&&t.default.chevronOpen),"aria-hidden":`true`,onClick:e=>{e.stopPropagation(),n.node.disabled||j(n.node.id)},children:(0,r.jsx)(i.ChevronRight,{size:14})}):(0,r.jsx)(`span`,{className:t.default.chevronPlaceholder,"aria-hidden":`true`}),n.node.icon?(0,r.jsx)(`span`,{className:t.default.icon,"aria-hidden":`true`,children:n.node.icon}):null,(0,r.jsx)(`span`,{className:t.default.label,children:n.node.label})]})},n.node.id)})})}exports.TreeView=o;
2
2
  //# sourceMappingURL=TreeView.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"TreeView.cjs","names":[],"sources":["../../../src/components/TreeView/TreeView.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — expansion and selection\n * are two independent controlled states, each with its uncontrolled twin\n * (expandedIds/defaultExpandedIds/onExpandedChange, selectedId/\n * defaultSelectedId/onSelect), plus toggleOnSelect for the case where they should\n * move together. The body is the flattened visible list and the roving focus over\n * it.\n */\nimport { ChevronRight } from \"lucide-react\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./TreeView.module.css\";\n\n/** One node of the tree. Children make it a branch; no children makes it a leaf. */\nexport interface TreeNode {\n /** Stable identifier, unique across the whole tree. */\n id: string;\n /** Rendered label. */\n label: ReactNode;\n /** Child nodes. An empty array still renders as a branch (an empty folder). */\n children?: TreeNode[];\n /** Icon rendered before the label. */\n icon?: ReactNode;\n /** Blocks selection and expansion, and skips the node in keyboard navigation. */\n disabled?: boolean;\n}\n\nexport interface TreeViewProps {\n /** Root nodes. */\n nodes: TreeNode[];\n /** Controlled expanded ids. */\n expandedIds?: string[];\n /** Uncontrolled initial expanded ids. */\n defaultExpandedIds?: string[];\n onExpandedChange?: (expandedIds: string[]) => void;\n /** Controlled selected id. `null` means nothing selected. */\n selectedId?: string | null;\n /** Uncontrolled initial selection. */\n defaultSelectedId?: string | null;\n onSelect?: (node: TreeNode) => void;\n /**\n * Selecting a branch also toggles it. Default `true` — matches how a file\n * explorer behaves; set `false` when a branch is itself a meaningful choice\n * (a category that owns items, for instance).\n */\n toggleOnSelect?: boolean;\n /** Accessible name for the tree. */\n label?: string;\n className?: string;\n}\n\ninterface FlatNode {\n node: TreeNode;\n depth: number;\n parentId: string | null;\n hasChildren: boolean;\n expanded: boolean;\n}\n\n/**\n * Walk the tree into the list of *currently visible* rows.\n *\n * Keyboard navigation works on this flattened view, which is what makes\n * ArrowDown from the last child of a collapsed-sibling branch land on the right\n * row: rows that are not rendered simply are not in the list.\n */\nfunction flatten(\n nodes: TreeNode[],\n expandedIds: Set<string>,\n depth = 0,\n parentId: string | null = null,\n out: FlatNode[] = [],\n): FlatNode[] {\n for (const node of nodes) {\n const hasChildren = Array.isArray(node.children);\n const expanded = hasChildren && expandedIds.has(node.id);\n out.push({ node, depth, parentId, hasChildren, expanded });\n if (expanded && node.children) {\n flatten(node.children, expandedIds, depth + 1, node.id, out);\n }\n }\n return out;\n}\n\n/**\n * Accessible tree for hierarchical data — categories, permissions, folders, an\n * org chart.\n *\n * Implements the `tree` role with **roving tabindex**: exactly one row is\n * tabbable, and the arrow keys move focus within the widget. That is what keeps a\n * 500-node tree from adding 500 stops to the page's tab order.\n *\n * Keyboard map: `↓`/`↑` move, `→` expands (or descends), `←` collapses (or goes to\n * the parent), `Home`/`End` jump to the first/last visible row, `Enter`/`Space`\n * select.\n *\n * The chevron is decoration (`aria-hidden`), not a button: the row itself carries\n * `aria-expanded`, so a second focusable control there would only add noise for a\n * screen reader while duplicating an action the keyboard map already has. It still\n * accepts a click, with the event stopped so it toggles without also selecting.\n *\n * @example\n * ```tsx\n * const nodes: TreeNode[] = [\n * {\n * id: \"vendas\",\n * label: \"Vendas\",\n * children: [\n * { id: \"vendas.ler\", label: \"Ler\" },\n * { id: \"vendas.editar\", label: \"Editar\" },\n * ],\n * },\n * { id: \"config\", label: \"Configurações\", children: [] },\n * ];\n *\n * <TreeView nodes={nodes} defaultExpandedIds={[\"vendas\"]} onSelect={(node) => console.log(node.id)} />\n * ```\n */\nexport function TreeView({\n nodes,\n expandedIds,\n defaultExpandedIds = [],\n onExpandedChange,\n selectedId,\n defaultSelectedId = null,\n onSelect,\n toggleOnSelect = true,\n label,\n className,\n}: TreeViewProps) {\n const expandedControlled = expandedIds !== undefined;\n const [internalExpanded, setInternalExpanded] = useState<string[]>(defaultExpandedIds);\n const expanded = expandedControlled ? expandedIds : internalExpanded;\n\n const selectionControlled = selectedId !== undefined;\n const [internalSelected, setInternalSelected] = useState<string | null>(defaultSelectedId);\n const selected = selectionControlled ? selectedId : internalSelected;\n\n const expandedSet = useMemo(() => new Set(expanded), [expanded]);\n const rows = useMemo(() => flatten(nodes, expandedSet), [nodes, expandedSet]);\n\n const [focusedId, setFocusedId] = useState<string | null>(null);\n const containerRef = useRef<HTMLUListElement>(null);\n\n const activeId =\n focusedId ?? selected ?? rows.find((row) => !row.node.disabled)?.node.id ?? null;\n\n const setExpanded = useCallback(\n (next: string[]): void => {\n if (!expandedControlled) setInternalExpanded(next);\n onExpandedChange?.(next);\n },\n [expandedControlled, onExpandedChange],\n );\n\n const toggle = useCallback(\n (id: string): void => {\n setExpanded(\n expanded.includes(id) ? expanded.filter((x) => x !== id) : [...expanded, id],\n );\n },\n [expanded, setExpanded],\n );\n\n const select = useCallback(\n (row: FlatNode): void => {\n if (row.node.disabled) return;\n if (!selectionControlled) setInternalSelected(row.node.id);\n setFocusedId(row.node.id);\n onSelect?.(row.node);\n if (row.hasChildren && toggleOnSelect) toggle(row.node.id);\n },\n [onSelect, selectionControlled, toggle, toggleOnSelect],\n );\n\n /** Move DOM focus to a row by id, so the roving tabindex actually roves. */\n const focusRow = useCallback((id: string): void => {\n setFocusedId(id);\n const element = containerRef.current?.querySelector<HTMLElement>(`[data-tree-id=\"${id}\"]`);\n element?.focus();\n }, []);\n\n const moveFocus = useCallback(\n (from: number, delta: number): void => {\n for (let index = from + delta; index >= 0 && index < rows.length; index += delta) {\n if (!rows[index].node.disabled) {\n focusRow(rows[index].node.id);\n return;\n }\n }\n },\n [rows, focusRow],\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>, row: FlatNode, index: number): void => {\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n moveFocus(index, 1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n moveFocus(index, -1);\n break;\n case \"ArrowRight\":\n event.preventDefault();\n if (row.hasChildren && !row.expanded) toggle(row.node.id);\n else if (row.expanded) moveFocus(index, 1);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n if (row.expanded) {\n toggle(row.node.id);\n } else if (row.parentId) {\n focusRow(row.parentId);\n }\n break;\n case \"Home\":\n event.preventDefault();\n moveFocus(-1, 1);\n break;\n case \"End\":\n event.preventDefault();\n moveFocus(rows.length, -1);\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n select(row);\n break;\n default:\n break;\n }\n },\n [moveFocus, rows.length, select, toggle, focusRow],\n );\n\n return (\n <ul\n ref={containerRef}\n role=\"tree\"\n aria-label={label}\n className={cn(styles.tree, className)}\n >\n {rows.map((row, index) => {\n const isSelected = selected === row.node.id;\n return (\n <li\n key={row.node.id}\n role=\"treeitem\"\n aria-expanded={row.hasChildren ? row.expanded : undefined}\n aria-selected={isSelected}\n aria-level={row.depth + 1}\n aria-disabled={row.node.disabled || undefined}\n className={styles.item}\n >\n <div\n data-tree-id={row.node.id}\n className={cn(\n styles.row,\n isSelected && styles.selected,\n row.node.disabled && styles.disabled,\n )}\n style={{\n paddingInlineStart: `calc(${row.depth} * var(--tempest-space-5))`,\n }}\n tabIndex={activeId === row.node.id && !row.node.disabled ? 0 : -1}\n onClick={() => select(row)}\n onFocus={() => setFocusedId(row.node.id)}\n onKeyDown={(event) => handleKeyDown(event, row, index)}\n >\n {row.hasChildren ? (\n <span\n className={cn(\n styles.chevron,\n row.expanded && styles.chevronOpen,\n )}\n aria-hidden=\"true\"\n onClick={(event) => {\n event.stopPropagation();\n if (!row.node.disabled) toggle(row.node.id);\n }}\n >\n <ChevronRight size={14} />\n </span>\n ) : (\n <span className={styles.chevronPlaceholder} aria-hidden=\"true\" />\n )}\n {row.node.icon ? (\n <span className={styles.icon} aria-hidden=\"true\">\n {row.node.icon}\n </span>\n ) : null}\n <span className={styles.label}>{row.node.label}</span>\n </div>\n </li>\n );\n })}\n </ul>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TreeView.cjs","names":[],"sources":["../../../src/components/TreeView/TreeView.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — expansion and selection\n * are two independent controlled states, each with its uncontrolled twin\n * (expandedIds/defaultExpandedIds/onExpandedChange, selectedId/\n * defaultSelectedId/onSelect), plus toggleOnSelect for the case where they should\n * move together. The body is the flattened visible list and the roving focus over\n * it.\n */\nimport { ChevronRight } from \"lucide-react\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./TreeView.module.css\";\n\n/** One node of the tree. Children make it a branch; no children makes it a leaf. */\nexport interface TreeNode {\n /** Stable identifier, unique across the whole tree. */\n id: string;\n /** Rendered label. */\n label: ReactNode;\n /** Child nodes. An empty array still renders as a branch (an empty folder). */\n children?: TreeNode[];\n /** Icon rendered before the label. */\n icon?: ReactNode;\n /** Blocks selection and expansion, and skips the node in keyboard navigation. */\n disabled?: boolean;\n}\n\nexport interface TreeViewProps {\n /** Root nodes. */\n nodes: TreeNode[];\n /** Controlled expanded ids. */\n expandedIds?: string[];\n /** Uncontrolled initial expanded ids. */\n defaultExpandedIds?: string[];\n onExpandedChange?: (expandedIds: string[]) => void;\n /** Controlled selected id. `null` means nothing selected. */\n selectedId?: string | null;\n /** Uncontrolled initial selection. */\n defaultSelectedId?: string | null;\n onSelect?: (node: TreeNode) => void;\n /**\n * Selecting a branch also toggles it. Default `true` — matches how a file\n * explorer behaves; set `false` when a branch is itself a meaningful choice\n * (a category that owns items, for instance).\n */\n toggleOnSelect?: boolean;\n /** Accessible name for the tree. */\n label?: string;\n className?: string;\n}\n\ninterface FlatNode {\n node: TreeNode;\n depth: number;\n parentId: string | null;\n hasChildren: boolean;\n expanded: boolean;\n}\n\n/**\n * Walk the tree into the list of *currently visible* rows.\n *\n * Keyboard navigation works on this flattened view, which is what makes\n * ArrowDown from the last child of a collapsed-sibling branch land on the right\n * row: rows that are not rendered simply are not in the list.\n */\nfunction flatten(\n nodes: TreeNode[],\n expandedIds: Set<string>,\n depth = 0,\n parentId: string | null = null,\n out: FlatNode[] = [],\n): FlatNode[] {\n for (const node of nodes) {\n const hasChildren = Array.isArray(node.children);\n const expanded = hasChildren && expandedIds.has(node.id);\n out.push({ node, depth, parentId, hasChildren, expanded });\n if (expanded && node.children) {\n flatten(node.children, expandedIds, depth + 1, node.id, out);\n }\n }\n return out;\n}\n\n/**\n * Accessible tree for hierarchical data — categories, permissions, folders, an\n * org chart.\n *\n * Implements the `tree` role with **roving tabindex**: exactly one row is\n * tabbable, and the arrow keys move focus within the widget. That is what keeps a\n * 500-node tree from adding 500 stops to the page's tab order.\n *\n * Keyboard map: `↓`/`↑` move, `→` expands (or descends), `←` collapses (or goes to\n * the parent), `Home`/`End` jump to the first/last visible row, `Enter`/`Space`\n * select.\n *\n * The chevron is decoration (`aria-hidden`), not a button: the row itself carries\n * `aria-expanded`, so a second focusable control there would only add noise for a\n * screen reader while duplicating an action the keyboard map already has. It still\n * accepts a click, with the event stopped so it toggles without also selecting.\n *\n * @example\n * ```tsx\n * const nodes: TreeNode[] = [\n * {\n * id: \"vendas\",\n * label: \"Vendas\",\n * children: [\n * { id: \"vendas.ler\", label: \"Ler\" },\n * { id: \"vendas.editar\", label: \"Editar\" },\n * ],\n * },\n * { id: \"config\", label: \"Configurações\", children: [] },\n * ];\n *\n * <TreeView nodes={nodes} defaultExpandedIds={[\"vendas\"]} onSelect={(node) => console.log(node.id)} />\n * ```\n */\nexport function TreeView({\n nodes,\n expandedIds,\n defaultExpandedIds = [],\n onExpandedChange,\n selectedId,\n defaultSelectedId = null,\n onSelect,\n toggleOnSelect = true,\n label,\n className,\n}: TreeViewProps) {\n const expandedControlled = expandedIds !== undefined;\n const [internalExpanded, setInternalExpanded] = useState<string[]>(defaultExpandedIds);\n const expanded = expandedControlled ? expandedIds : internalExpanded;\n\n const selectionControlled = selectedId !== undefined;\n const [internalSelected, setInternalSelected] = useState<string | null>(defaultSelectedId);\n const selected = selectionControlled ? selectedId : internalSelected;\n\n const expandedSet = useMemo(() => new Set(expanded), [expanded]);\n const rows = useMemo(() => flatten(nodes, expandedSet), [nodes, expandedSet]);\n\n const [focusedId, setFocusedId] = useState<string | null>(null);\n const containerRef = useRef<HTMLUListElement>(null);\n\n const activeId =\n focusedId ?? selected ?? rows.find((row) => !row.node.disabled)?.node.id ?? null;\n\n const setExpanded = useCallback(\n (next: string[]): void => {\n if (!expandedControlled) setInternalExpanded(next);\n onExpandedChange?.(next);\n },\n [expandedControlled, onExpandedChange],\n );\n\n const toggle = useCallback(\n (id: string): void => {\n setExpanded(\n expanded.includes(id) ? expanded.filter((x) => x !== id) : [...expanded, id],\n );\n },\n [expanded, setExpanded],\n );\n\n const select = useCallback(\n (row: FlatNode): void => {\n if (row.node.disabled) return;\n if (!selectionControlled) setInternalSelected(row.node.id);\n setFocusedId(row.node.id);\n onSelect?.(row.node);\n if (row.hasChildren && toggleOnSelect) toggle(row.node.id);\n },\n [onSelect, selectionControlled, toggle, toggleOnSelect],\n );\n\n /** Move DOM focus to a row by id, so the roving tabindex actually roves. */\n const focusRow = useCallback((id: string): void => {\n setFocusedId(id);\n const element = containerRef.current?.querySelector<HTMLElement>(`[data-tree-id=\"${id}\"]`);\n element?.focus();\n }, []);\n\n const moveFocus = useCallback(\n (from: number, delta: number): void => {\n for (let index = from + delta; index >= 0 && index < rows.length; index += delta) {\n if (!rows[index].node.disabled) {\n focusRow(rows[index].node.id);\n return;\n }\n }\n },\n [rows, focusRow],\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>, row: FlatNode, index: number): void => {\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n moveFocus(index, 1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n moveFocus(index, -1);\n break;\n case \"ArrowRight\":\n event.preventDefault();\n if (row.hasChildren && !row.expanded) toggle(row.node.id);\n else if (row.expanded) moveFocus(index, 1);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n if (row.expanded) {\n toggle(row.node.id);\n } else if (row.parentId) {\n focusRow(row.parentId);\n }\n break;\n case \"Home\":\n event.preventDefault();\n moveFocus(-1, 1);\n break;\n case \"End\":\n event.preventDefault();\n moveFocus(rows.length, -1);\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n select(row);\n break;\n default:\n break;\n }\n },\n [moveFocus, rows.length, select, toggle, focusRow],\n );\n\n return (\n <ul\n ref={containerRef}\n role=\"tree\"\n aria-label={label}\n className={cn(styles.tree, className)}\n >\n {rows.map((row, index) => {\n const isSelected = selected === row.node.id;\n return (\n <li\n key={row.node.id}\n role=\"treeitem\"\n aria-expanded={row.hasChildren ? row.expanded : undefined}\n aria-selected={isSelected}\n aria-level={row.depth + 1}\n aria-disabled={row.node.disabled || undefined}\n className={styles.item}\n >\n <div\n data-tree-id={row.node.id}\n className={cn(\n styles.row,\n isSelected && styles.selected,\n row.node.disabled && styles.disabled,\n )}\n style={{\n paddingInlineStart: `calc(${row.depth} * var(--tempest-space-5))`,\n }}\n tabIndex={activeId === row.node.id && !row.node.disabled ? 0 : -1}\n onClick={() => select(row)}\n onFocus={() => setFocusedId(row.node.id)}\n onKeyDown={(event) => handleKeyDown(event, row, index)}\n >\n {row.hasChildren ? (\n <span\n className={cn(\n styles.chevron,\n row.expanded && styles.chevronOpen,\n )}\n aria-hidden=\"true\"\n onClick={(event) => {\n event.stopPropagation();\n if (!row.node.disabled) toggle(row.node.id);\n }}\n >\n <ChevronRight size={14} />\n </span>\n ) : (\n <span className={styles.chevronPlaceholder} aria-hidden=\"true\" />\n )}\n {row.node.icon ? (\n <span className={styles.icon} aria-hidden=\"true\">\n {row.node.icon}\n </span>\n ) : null}\n <span className={styles.label}>{row.node.label}</span>\n </div>\n </li>\n );\n })}\n </ul>\n );\n}\n"],"mappings":"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"}
@@ -57,10 +57,7 @@ function u({ nodes: u, expandedIds: d, defaultExpandedIds: f = [], onExpandedCha
57
57
  e.preventDefault(), R(k.length, -1);
58
58
  break;
59
59
  case "Enter":
60
- case " ":
61
- e.preventDefault(), I(t);
62
- break;
63
- default: break;
60
+ case " ": e.preventDefault(), I(t);
64
61
  }
65
62
  }, [
66
63
  R,