@stackcraftmx/calcita 0.23.0 → 0.25.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 (359) hide show
  1. package/README.md +111 -22
  2. package/cli/projectFiles.js +5 -3
  3. package/cli/templates/project/README.md.tpl +28 -0
  4. package/cli/templates/project/public/manifest.webmanifest.tpl +6 -6
  5. package/cli/templates/project/src/app/AppProviders.jsx.tpl +9 -4
  6. package/cli/templates/project/src/app/appRouter.jsx.tpl +4 -2
  7. package/cli/templates/project/src/app/appSettings.js.tpl +14 -0
  8. package/cli/templates/project/src/core/AppLogo.jsx.tpl +3 -1
  9. package/cli/templates/project/src/core/index.js.tpl +1 -0
  10. package/cli/templates/project/src/core/publicPaths.js.tpl +24 -0
  11. package/cli/templates/project/vite.config.js.tpl +27 -1
  12. package/dist/DataTable/AuditTrailTable.js +5 -3
  13. package/dist/DataTable/AuditTrailTable.js.map +1 -1
  14. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +2 -1
  15. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
  16. package/dist/DataTable/EmptyState.js +2 -2
  17. package/dist/DataTable/EmptyState.js.map +1 -1
  18. package/dist/DataTable/FilterChips.js +2 -2
  19. package/dist/DataTable/FilterChips.js.map +1 -1
  20. package/dist/DataTable/GoToPage.js +4 -3
  21. package/dist/DataTable/GoToPage.js.map +1 -1
  22. package/dist/DataTable/ServerTable.js +153 -83
  23. package/dist/DataTable/ServerTable.js.map +1 -1
  24. package/dist/DataTable/rowSelection.js +61 -0
  25. package/dist/DataTable/rowSelection.js.map +1 -0
  26. package/dist/access/PermissionMatrix.js +3 -2
  27. package/dist/access/PermissionMatrix.js.map +1 -1
  28. package/dist/account/AccountSecuritySection.js +2 -2
  29. package/dist/account/AccountSecuritySection.js.map +1 -1
  30. package/dist/account/ForgotPasswordScreen.js +5 -4
  31. package/dist/account/ForgotPasswordScreen.js.map +1 -1
  32. package/dist/account/ProfileScreen.js +39 -13
  33. package/dist/account/ProfileScreen.js.map +1 -1
  34. package/dist/account/ResetPasswordScreen.js +3 -2
  35. package/dist/account/ResetPasswordScreen.js.map +1 -1
  36. package/dist/account/SignInScreen.js +7 -6
  37. package/dist/account/SignInScreen.js.map +1 -1
  38. package/dist/account/accountConnector.js.map +1 -1
  39. package/dist/account/restAccountConnector.js +2 -1
  40. package/dist/account/restAccountConnector.js.map +1 -1
  41. package/dist/adapters/reactRouterRoutes.js +7 -6
  42. package/dist/adapters/reactRouterRoutes.js.map +1 -1
  43. package/dist/api/createApiClient.js +35 -5
  44. package/dist/api/createApiClient.js.map +1 -1
  45. package/dist/appShell/AppLayout.js +27 -7
  46. package/dist/appShell/AppLayout.js.map +1 -1
  47. package/dist/appShell/AppearanceSettings.js +29 -6
  48. package/dist/appShell/AppearanceSettings.js.map +1 -1
  49. package/dist/appShell/AuthLayout.js +2 -0
  50. package/dist/appShell/AuthLayout.js.map +1 -1
  51. package/dist/appShell/BarActionButton.js +3 -3
  52. package/dist/appShell/BarActionButton.js.map +1 -1
  53. package/dist/appShell/BarActions.js +19 -8
  54. package/dist/appShell/BarActions.js.map +1 -1
  55. package/dist/appShell/BarCapsule.js +19 -4
  56. package/dist/appShell/BarCapsule.js.map +1 -1
  57. package/dist/appShell/BarSearch.js +92 -0
  58. package/dist/appShell/BarSearch.js.map +1 -0
  59. package/dist/appShell/MobileTabBar.js +3 -1
  60. package/dist/appShell/MobileTabBar.js.map +1 -1
  61. package/dist/appShell/shellSizes.js +2 -0
  62. package/dist/appShell/shellSizes.js.map +1 -1
  63. package/dist/auth/PasswordChecklist.js +2 -2
  64. package/dist/auth/PasswordChecklist.js.map +1 -1
  65. package/dist/calendar/EventCalendar.js +16 -5
  66. package/dist/calendar/EventCalendar.js.map +1 -1
  67. package/dist/calendar/EventDetails.js +74 -0
  68. package/dist/calendar/EventDetails.js.map +1 -0
  69. package/dist/calendar/calendarEvents.js +31 -0
  70. package/dist/calendar/calendarEvents.js.map +1 -0
  71. package/dist/calendar.js +8 -1
  72. package/dist/calendar.js.map +1 -1
  73. package/dist/charts.js +10 -1
  74. package/dist/charts.js.map +1 -1
  75. package/dist/datePickers/DateInput.js +3 -2
  76. package/dist/datePickers/DateInput.js.map +1 -1
  77. package/dist/downloads/DownloadManager.js +14 -20
  78. package/dist/downloads/DownloadManager.js.map +1 -1
  79. package/dist/downloads/downloadQueue.js +4 -2
  80. package/dist/downloads/downloadQueue.js.map +1 -1
  81. package/dist/downloads/downloadsMachine.js +4 -1
  82. package/dist/downloads/downloadsMachine.js.map +1 -1
  83. package/dist/downloads/fileTypes.js +22 -0
  84. package/dist/downloads/fileTypes.js.map +1 -0
  85. package/dist/feedback/ConfirmDialog.js +3 -2
  86. package/dist/feedback/ConfirmDialog.js.map +1 -1
  87. package/dist/feedback/ModalWaiting.js +2 -2
  88. package/dist/feedback/ModalWaiting.js.map +1 -1
  89. package/dist/feedback/NewBuildNotice.js +2 -2
  90. package/dist/feedback/NewBuildNotice.js.map +1 -1
  91. package/dist/filepond/FileDropzone.js +5 -3
  92. package/dist/filepond/FileDropzone.js.map +1 -1
  93. package/dist/formatting.js +171 -143
  94. package/dist/formatting.js.map +1 -1
  95. package/dist/forms/FormWizard.js +18 -45
  96. package/dist/forms/FormWizard.js.map +1 -1
  97. package/dist/forms/auth/ChangePasswordForm.js +5 -4
  98. package/dist/forms/auth/ChangePasswordForm.js.map +1 -1
  99. package/dist/forms/auth/ForgotPasswordForm.js +2 -2
  100. package/dist/forms/auth/ForgotPasswordForm.js.map +1 -1
  101. package/dist/forms/auth/SignInForm.js +2 -2
  102. package/dist/forms/auth/SignInForm.js.map +1 -1
  103. package/dist/forms/security/SecondStepForm.js +2 -2
  104. package/dist/forms/security/SecondStepForm.js.map +1 -1
  105. package/dist/forms/security/TwoStepVerificationCard.js +8 -6
  106. package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
  107. package/dist/hookForm/FormWizard.js +59 -0
  108. package/dist/hookForm/FormWizard.js.map +1 -0
  109. package/dist/inbox/InboxLayout.js +44 -8
  110. package/dist/inbox/InboxLayout.js.map +1 -1
  111. package/dist/inbox/MessageList.js +71 -21
  112. package/dist/inbox/MessageList.js.map +1 -1
  113. package/dist/inbox/MessageView.js +80 -29
  114. package/dist/inbox/MessageView.js.map +1 -1
  115. package/dist/inbox/inboxDates.js +4 -3
  116. package/dist/inbox/inboxDates.js.map +1 -1
  117. package/dist/index.js +157 -133
  118. package/dist/index.js.map +1 -1
  119. package/dist/inputs/CatalogInput.js +8 -4
  120. package/dist/inputs/CatalogInput.js.map +1 -1
  121. package/dist/inputs/ColorInput.js +3 -2
  122. package/dist/inputs/ColorInput.js.map +1 -1
  123. package/dist/inputs/FormSection.js +3 -3
  124. package/dist/inputs/FormSection.js.map +1 -1
  125. package/dist/inputs/NumberInput.js +3 -2
  126. package/dist/inputs/NumberInput.js.map +1 -1
  127. package/dist/inputs/SelectInput.js +29 -11
  128. package/dist/inputs/SelectInput.js.map +1 -1
  129. package/dist/inputs/TextInput.js +6 -2
  130. package/dist/inputs/TextInput.js.map +1 -1
  131. package/dist/inputs/WizardLayout.js +54 -0
  132. package/dist/inputs/WizardLayout.js.map +1 -0
  133. package/dist/kanban/KanbanBoard.js +239 -14
  134. package/dist/kanban/KanbanBoard.js.map +1 -1
  135. package/dist/kanban/kanbanCards.js +19 -0
  136. package/dist/kanban/kanbanCards.js.map +1 -0
  137. package/dist/kanban.js +7 -1
  138. package/dist/kanban.js.map +1 -1
  139. package/dist/locales/en.js +74 -1
  140. package/dist/locales/en.js.map +1 -1
  141. package/dist/locales/es.js +74 -1
  142. package/dist/locales/es.js.map +1 -1
  143. package/dist/maps/MapView.js +28 -8
  144. package/dist/maps/MapView.js.map +1 -1
  145. package/dist/observability/usePublishedBuildWatch.js +4 -3
  146. package/dist/observability/usePublishedBuildWatch.js.map +1 -1
  147. package/dist/pwa/InstallAppSetting.js +6 -5
  148. package/dist/pwa/InstallAppSetting.js.map +1 -1
  149. package/dist/pwa/InstallInstructionsDialog.js +2 -2
  150. package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
  151. package/dist/pwa/InstallPromptBanner.js +15 -9
  152. package/dist/pwa/InstallPromptBanner.js.map +1 -1
  153. package/dist/pwa/useInstallPrompt.js +2 -1
  154. package/dist/pwa/useInstallPrompt.js.map +1 -1
  155. package/dist/react-hook-form.js +6 -4
  156. package/dist/react-hook-form.js.map +1 -1
  157. package/dist/security/ActivityLog.js +4 -3
  158. package/dist/security/ActivityLog.js.map +1 -1
  159. package/dist/security/RecoveryCodesDialog.js +4 -3
  160. package/dist/security/RecoveryCodesDialog.js.map +1 -1
  161. package/dist/security/SessionsCard.js +8 -6
  162. package/dist/security/SessionsCard.js.map +1 -1
  163. package/dist/table.js +2 -0
  164. package/dist/table.js.map +1 -1
  165. package/dist/theme/components/MuiDialog.js +13 -0
  166. package/dist/theme/components/MuiDialog.js.map +1 -0
  167. package/dist/theme/createToolkitTheme.js.map +1 -1
  168. package/dist/theme/toolkitComponents.js +2 -0
  169. package/dist/theme/toolkitComponents.js.map +1 -1
  170. package/dist/toolkit/ComponentTexts.js +13 -0
  171. package/dist/toolkit/ComponentTexts.js.map +1 -0
  172. package/dist/toolkit/ToolkitProvider.js +36 -28
  173. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  174. package/dist/toolkit/browserNavigation.js +19 -20
  175. package/dist/toolkit/browserNavigation.js.map +1 -1
  176. package/dist/toolkit/componentTextsContext.js +15 -0
  177. package/dist/toolkit/componentTextsContext.js.map +1 -0
  178. package/dist/toolkit/hashNavigation.js +40 -0
  179. package/dist/toolkit/hashNavigation.js.map +1 -0
  180. package/dist/toolkit/navigationPaths.js +49 -0
  181. package/dist/toolkit/navigationPaths.js.map +1 -0
  182. package/dist/toolkit/toolkitSettings.js +24 -4
  183. package/dist/toolkit/toolkitSettings.js.map +1 -1
  184. package/dist/toolkit/useFormatters.js +12 -0
  185. package/dist/toolkit/useFormatters.js.map +1 -0
  186. package/dist/toolkit/useToolkitTranslation.js +11 -7
  187. package/dist/toolkit/useToolkitTranslation.js.map +1 -1
  188. package/dist/types/DataTable/AuditTrailTable.d.ts +3 -1
  189. package/dist/types/DataTable/EmptyState.d.ts +7 -2
  190. package/dist/types/DataTable/FilterChips.d.ts +3 -1
  191. package/dist/types/DataTable/GoToPage.d.ts +7 -2
  192. package/dist/types/DataTable/SelectionBar.d.ts +32 -0
  193. package/dist/types/DataTable/ServerTable.d.ts +64 -3
  194. package/dist/types/DataTable/index.d.ts +1 -1
  195. package/dist/types/DataTable/rowSelection.d.ts +113 -0
  196. package/dist/types/access/PermissionMatrix.d.ts +3 -1
  197. package/dist/types/account/AccountSecuritySection.d.ts +3 -1
  198. package/dist/types/account/ForgotPasswordScreen.d.ts +3 -1
  199. package/dist/types/account/ProfileScreen.d.ts +31 -1
  200. package/dist/types/account/ResetPasswordScreen.d.ts +3 -1
  201. package/dist/types/account/SignInScreen.d.ts +3 -1
  202. package/dist/types/account/accountConnector.d.ts +7 -2
  203. package/dist/types/adapters/reactRouterRoutes.d.ts +14 -2
  204. package/dist/types/api/createApiClient.d.ts +29 -1
  205. package/dist/types/appShell/AppLayout.d.ts +11 -1
  206. package/dist/types/appShell/AppearanceSettings.d.ts +8 -1
  207. package/dist/types/appShell/BarActionButton.d.ts +2 -2
  208. package/dist/types/appShell/BarActions.d.ts +6 -1
  209. package/dist/types/appShell/BarCapsule.d.ts +8 -0
  210. package/dist/types/appShell/BarSearch.d.ts +24 -0
  211. package/dist/types/appShell/MobileTabBar.d.ts +4 -1
  212. package/dist/types/appShell/shellSizes.d.ts +1 -0
  213. package/dist/types/auth/PasswordChecklist.d.ts +3 -1
  214. package/dist/types/calendar/EventCalendar.d.ts +18 -1
  215. package/dist/types/calendar/EventDetails.d.ts +36 -0
  216. package/dist/types/calendar/calendarEvents.d.ts +27 -0
  217. package/dist/types/calendar.d.ts +2 -0
  218. package/dist/types/charts.d.ts +4 -0
  219. package/dist/types/datePickers/DateInput.d.ts +3 -1
  220. package/dist/types/downloads/DownloadManager.d.ts +7 -1
  221. package/dist/types/downloads/downloadQueue.d.ts +8 -3
  222. package/dist/types/downloads/fileTypes.d.ts +39 -0
  223. package/dist/types/downloads/index.d.ts +1 -0
  224. package/dist/types/feedback/ConfirmDialog.d.ts +3 -1
  225. package/dist/types/feedback/ModalWaiting.d.ts +7 -2
  226. package/dist/types/feedback/NewBuildNotice.d.ts +3 -1
  227. package/dist/types/filepond/FileDropzone.d.ts +3 -1
  228. package/dist/types/formatting.d.ts +45 -90
  229. package/dist/types/forms/FormWizard.d.ts +3 -1
  230. package/dist/types/forms/auth/ChangePasswordForm.d.ts +3 -1
  231. package/dist/types/forms/auth/ForgotPasswordForm.d.ts +3 -1
  232. package/dist/types/forms/auth/SignInForm.d.ts +3 -1
  233. package/dist/types/forms/security/SecondStepForm.d.ts +3 -1
  234. package/dist/types/forms/security/TwoStepVerificationCard.d.ts +3 -1
  235. package/dist/types/hookForm/FormWizard.d.ts +22 -0
  236. package/dist/types/inbox/InboxLayout.d.ts +14 -2
  237. package/dist/types/inbox/MessageList.d.ts +30 -5
  238. package/dist/types/inbox/MessageView.d.ts +35 -3
  239. package/dist/types/inbox/inboxDates.d.ts +6 -1
  240. package/dist/types/index.d.ts +12 -5
  241. package/dist/types/inputs/CatalogInput.d.ts +9 -5
  242. package/dist/types/inputs/ColorInput.d.ts +3 -1
  243. package/dist/types/inputs/FormSection.d.ts +2 -2
  244. package/dist/types/inputs/NumberInput.d.ts +3 -1
  245. package/dist/types/inputs/SelectInput.d.ts +9 -5
  246. package/dist/types/inputs/TextInput.d.ts +3 -1
  247. package/dist/types/inputs/WizardLayout.d.ts +34 -0
  248. package/dist/types/kanban/KanbanBoard.d.ts +61 -6
  249. package/dist/types/kanban/kanbanCards.d.ts +22 -0
  250. package/dist/types/kanban.d.ts +1 -0
  251. package/dist/types/locales/en.d.ts +77 -0
  252. package/dist/types/locales/es.d.ts +77 -0
  253. package/dist/types/maps/MapView.d.ts +12 -3
  254. package/dist/types/observability/usePublishedBuildWatch.d.ts +3 -1
  255. package/dist/types/pwa/InstallAppSetting.d.ts +7 -1
  256. package/dist/types/pwa/InstallInstructionsDialog.d.ts +3 -1
  257. package/dist/types/pwa/InstallPromptBanner.d.ts +10 -1
  258. package/dist/types/pwa/useInstallPrompt.d.ts +3 -2
  259. package/dist/types/react-hook-form.d.ts +1 -0
  260. package/dist/types/security/ActivityLog.d.ts +3 -1
  261. package/dist/types/security/RecoveryCodesDialog.d.ts +6 -1
  262. package/dist/types/security/SessionsCard.d.ts +3 -1
  263. package/dist/types/table.d.ts +3 -1
  264. package/dist/types/theme/components/MuiDialog.d.ts +9 -0
  265. package/dist/types/toolkit/ComponentTexts.d.ts +11 -0
  266. package/dist/types/toolkit/ToolkitProvider.d.ts +18 -2
  267. package/dist/types/toolkit/browserNavigation.d.ts +23 -28
  268. package/dist/types/toolkit/componentTextsContext.d.ts +20 -0
  269. package/dist/types/toolkit/hashNavigation.d.ts +12 -0
  270. package/dist/types/toolkit/navigationPaths.d.ts +43 -0
  271. package/dist/types/toolkit/toolkitSettings.d.ts +49 -4
  272. package/dist/types/toolkit/useFormatters.d.ts +6 -0
  273. package/dist/types/toolkit/useToolkitTranslation.d.ts +2 -1
  274. package/dist/types/ui/actions/ActionsMenu.d.ts +20 -0
  275. package/dist/types/ui/actions/CopyButton.d.ts +3 -1
  276. package/dist/types/ui/actions/DownloadMenu.d.ts +8 -3
  277. package/dist/types/ui/actions/SecretField.d.ts +5 -1
  278. package/dist/types/ui/actions/SelectionActions.d.ts +39 -0
  279. package/dist/types/ui/filters/DateRangeFilter.d.ts +3 -1
  280. package/dist/types/ui/filters/QuickFilters.d.ts +3 -1
  281. package/dist/types/ui/index.d.ts +1 -1
  282. package/dist/types/ui/labels/tagOf.d.ts +13 -0
  283. package/dist/types/ui/layout/CollapsibleSection.d.ts +4 -1
  284. package/dist/types/ui/layout/ContentDialog.d.ts +3 -1
  285. package/dist/types/ui/layout/ErrorPage.d.ts +14 -1
  286. package/dist/types/ui/layout/FullPageLoader.d.ts +7 -1
  287. package/dist/types/ui/layout/PageHeading.d.ts +3 -1
  288. package/dist/types/ui/metrics/BarList.d.ts +7 -1
  289. package/dist/types/ui/metrics/CalendarHeatmap.d.ts +7 -1
  290. package/dist/types/ui/metrics/ChartTooltip.d.ts +9 -3
  291. package/dist/types/ui/metrics/ColumnChart.d.ts +23 -11
  292. package/dist/types/ui/metrics/LineChart.d.ts +41 -0
  293. package/dist/types/ui/metrics/MetricCard.d.ts +9 -1
  294. package/dist/types/ui/metrics/TableToggle.d.ts +9 -2
  295. package/dist/types/ui/metrics/chartParts.d.ts +112 -0
  296. package/dist/types/ui/navigation/BackButton.d.ts +3 -1
  297. package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +8 -2
  298. package/dist/types/ui/navigation/FontSizeToggle.d.ts +7 -2
  299. package/dist/types/ui/navigation/LanguageToggle.d.ts +8 -3
  300. package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +5 -2
  301. package/dist/types/ui/timeline/GroupedTimeline.d.ts +4 -1
  302. package/dist/types/upload/FileDropzone.d.ts +3 -1
  303. package/dist/ui/actions/ActionsMenu.js +51 -0
  304. package/dist/ui/actions/ActionsMenu.js.map +1 -0
  305. package/dist/ui/actions/CopyButton.js +2 -2
  306. package/dist/ui/actions/CopyButton.js.map +1 -1
  307. package/dist/ui/actions/DownloadMenu.js +11 -12
  308. package/dist/ui/actions/DownloadMenu.js.map +1 -1
  309. package/dist/ui/actions/SecretField.js +8 -4
  310. package/dist/ui/actions/SecretField.js.map +1 -1
  311. package/dist/ui/actions/SelectionActions.js +142 -0
  312. package/dist/ui/actions/SelectionActions.js.map +1 -0
  313. package/dist/ui/filters/DateRangeFilter.js +4 -3
  314. package/dist/ui/filters/DateRangeFilter.js.map +1 -1
  315. package/dist/ui/filters/QuickFilters.js +2 -2
  316. package/dist/ui/filters/QuickFilters.js.map +1 -1
  317. package/dist/ui/labels/tagOf.js +7 -0
  318. package/dist/ui/labels/tagOf.js.map +1 -0
  319. package/dist/ui/layout/CollapsibleSection.js +6 -2
  320. package/dist/ui/layout/CollapsibleSection.js.map +1 -1
  321. package/dist/ui/layout/ContentDialog.js +2 -2
  322. package/dist/ui/layout/ContentDialog.js.map +1 -1
  323. package/dist/ui/layout/ErrorPage.js +21 -7
  324. package/dist/ui/layout/ErrorPage.js.map +1 -1
  325. package/dist/ui/layout/FullPageLoader.js +2 -2
  326. package/dist/ui/layout/FullPageLoader.js.map +1 -1
  327. package/dist/ui/layout/PageHeading.js +2 -2
  328. package/dist/ui/layout/PageHeading.js.map +1 -1
  329. package/dist/ui/metrics/BarList.js +18 -13
  330. package/dist/ui/metrics/BarList.js.map +1 -1
  331. package/dist/ui/metrics/CalendarHeatmap.js +11 -6
  332. package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
  333. package/dist/ui/metrics/ChartTooltip.js +1 -1
  334. package/dist/ui/metrics/ChartTooltip.js.map +1 -1
  335. package/dist/ui/metrics/ColumnChart.js +59 -63
  336. package/dist/ui/metrics/ColumnChart.js.map +1 -1
  337. package/dist/ui/metrics/LineChart.js +117 -0
  338. package/dist/ui/metrics/LineChart.js.map +1 -0
  339. package/dist/ui/metrics/MetricCard.js +41 -8
  340. package/dist/ui/metrics/MetricCard.js.map +1 -1
  341. package/dist/ui/metrics/TableToggle.js +4 -2
  342. package/dist/ui/metrics/TableToggle.js.map +1 -1
  343. package/dist/ui/metrics/chartParts.js +93 -0
  344. package/dist/ui/metrics/chartParts.js.map +1 -0
  345. package/dist/ui/navigation/BackButton.js +2 -2
  346. package/dist/ui/navigation/BackButton.js.map +1 -1
  347. package/dist/ui/navigation/ColorSchemeToggle.js +6 -3
  348. package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
  349. package/dist/ui/navigation/FontSizeToggle.js +3 -2
  350. package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
  351. package/dist/ui/navigation/LanguageToggle.js +40 -19
  352. package/dist/ui/navigation/LanguageToggle.js.map +1 -1
  353. package/dist/ui/navigation/PageBreadcrumbs.js +2 -2
  354. package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
  355. package/dist/ui/timeline/GroupedTimeline.js +10 -8
  356. package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
  357. package/dist/upload/FileDropzone.js +5 -3
  358. package/dist/upload/FileDropzone.js.map +1 -1
  359. package/package.json +14 -6
@@ -16,8 +16,10 @@
16
16
  * @param {import('react').ReactNode} props.groups[].items[].title
17
17
  * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas
18
18
  * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)
19
+ * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,
20
+ * para pedir sus eventos hasta que se abre)
19
21
  */
20
- export default function GroupedTimeline({ groups }: {
22
+ export default function GroupedTimeline({ groups, onGroupToggle }: {
21
23
  groups: {
22
24
  key: string;
23
25
  title?: string;
@@ -35,4 +37,5 @@ export default function GroupedTimeline({ groups }: {
35
37
  details?: import('react').ReactNode;
36
38
  }[];
37
39
  }[];
40
+ onGroupToggle?: (groupKey: string, isExpanded: boolean) => void;
38
41
  }): import("react").JSX.Element;
@@ -13,8 +13,9 @@
13
13
  * @param {import('react').ReactNode} [props.actions]
14
14
  * @param {import('react').ReactNode} [props.children]
15
15
  * @param {object} [props.sx] ajustes del recuadro
16
+ * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete
16
17
  */
17
- export default function FileDropzone({ value, onChange, accept, maxSize, hint, buttonLabel, disabled, busy, busyModal, onReject, actions, children, sx, }: {
18
+ export default function FileDropzone({ value, onChange, accept, maxSize, hint, buttonLabel, disabled, busy, busyModal, onReject, actions, children, sx, texts, }: {
18
19
  value?: File | null;
19
20
  onChange: (file: File | null) => void;
20
21
  accept?: string | string[];
@@ -31,4 +32,5 @@ export default function FileDropzone({ value, onChange, accept, maxSize, hint, b
31
32
  actions?: import('react').ReactNode;
32
33
  children?: import('react').ReactNode;
33
34
  sx?: object;
35
+ texts?: object;
34
36
  }): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsxs, Fragment, jsx } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import IconButton from "@mui/material/IconButton";
5
+ import ListItemIcon from "@mui/material/ListItemIcon";
6
+ import ListItemText from "@mui/material/ListItemText";
7
+ import Menu from "@mui/material/Menu";
8
+ import MenuItem from "@mui/material/MenuItem";
9
+ import Tooltip from "@mui/material/Tooltip";
10
+ import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
11
+ function ActionsMenu({ label, actions, icon, size = "small" }) {
12
+ const [anchor, setAnchor] = useState(null);
13
+ if (!actions?.length) {
14
+ return null;
15
+ }
16
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
17
+ /* @__PURE__ */ jsx(Tooltip, { title: label, children: /* @__PURE__ */ jsx(
18
+ IconButton,
19
+ {
20
+ size,
21
+ "aria-label": label,
22
+ "aria-haspopup": "menu",
23
+ onClick: (event) => {
24
+ event.stopPropagation();
25
+ setAnchor(event.currentTarget);
26
+ },
27
+ children: icon ?? /* @__PURE__ */ jsx(MoreHorizRoundedIcon, { fontSize: "small" })
28
+ }
29
+ ) }),
30
+ /* @__PURE__ */ jsx(Menu, { anchorEl: anchor, open: Boolean(anchor), onClose: () => setAnchor(null), onClick: (event) => event.stopPropagation(), children: actions.map((action) => /* @__PURE__ */ jsxs(
31
+ MenuItem,
32
+ {
33
+ disabled: action.disabled,
34
+ onClick: () => {
35
+ setAnchor(null);
36
+ action.onClick();
37
+ },
38
+ sx: action.color ? { color: `${action.color}.main` } : void 0,
39
+ children: [
40
+ action.icon && /* @__PURE__ */ jsx(ListItemIcon, { sx: action.color ? { color: "inherit" } : void 0, children: action.icon }),
41
+ /* @__PURE__ */ jsx(ListItemText, { children: action.label })
42
+ ]
43
+ },
44
+ action.key
45
+ )) })
46
+ ] });
47
+ }
48
+ export {
49
+ ActionsMenu as default
50
+ };
51
+ //# sourceMappingURL=ActionsMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ActionsMenu.js","sources":["../../../src/ui/actions/ActionsMenu.jsx"],"sourcesContent":["// Un botón «⋯» con un menú de acciones: el de cada tarjeta del tablero, el de cada columna, el de cada mensaje de la bandeja.\n// Las acciones las da la pantalla (label ya traducido, ícono, color, apagada); sin acciones no se pinta nada.\n//\n// <ActionsMenu label=\"Acciones de «Junta de cierre»\"\n// actions={[{key: 'delete', label: 'Borrar', icon: <DeleteOutlineIcon fontSize=\"small\" />, color: 'error', onClick: remove}]} />\n//\n// El clic no llega a lo de abajo: no levanta una tarjeta que se arrastra ni abre el renglón donde vive.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\n/**\n * @param {object} props\n * @param {string} props.label el nombre del botón (lector de pantalla y su tooltip), ya traducido\n * @param {Array<{key: string, label: string, icon?: import('react').ReactNode, color?: string, disabled?: boolean, onClick: () => void}>} [props.actions]\n * @param {import('react').ReactNode} [props.icon] otro ícono para el botón (por omisión «⋯»)\n * @param {'small'|'medium'} [props.size]\n */\nexport default function ActionsMenu({label, actions, icon, size = 'small'}) {\n const [anchor, setAnchor] = useState(null);\n if (!actions?.length) {\n return null;\n }\n return (\n <>\n <Tooltip title={label}>\n <IconButton size={size} aria-label={label} aria-haspopup=\"menu\"\n onClick={(event) => {\n event.stopPropagation();\n setAnchor(event.currentTarget);\n }}>\n {icon ?? <MoreHorizRoundedIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n <Menu anchorEl={anchor} open={Boolean(anchor)} onClose={() => setAnchor(null)} onClick={(event) => event.stopPropagation()}>\n {actions.map((action) => (\n <MenuItem key={action.key} disabled={action.disabled}\n onClick={() => {\n setAnchor(null);\n action.onClick();\n }}\n sx={action.color ? {color: `${action.color}.main`} : undefined}>\n {action.icon && <ListItemIcon sx={action.color ? {color: 'inherit'} : undefined}>{action.icon}</ListItemIcon>}\n <ListItemText>{action.label}</ListItemText>\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAyBA,SAAwB,YAAY,EAAC,OAAO,SAAS,MAAM,OAAO,WAAU;AACxE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,IAAI;AACzC,MAAI,CAAC,SAAS,QAAQ;AAClB,WAAO;AAAA,EACX;AACA,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAQ,OAAO,OACZ,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAW;AAAA,QAAY,cAAY;AAAA,QAAO,iBAAc;AAAA,QAC7C,SAAS,CAAC,UAAU;AAChB,gBAAM,gBAAA;AACN,oBAAU,MAAM,aAAa;AAAA,QACjC;AAAA,QACP,UAAA,QAAQ,oBAAC,sBAAA,EAAqB,UAAS,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA,GAExD;AAAA,IACA,oBAAC,QAAK,UAAU,QAAQ,MAAM,QAAQ,MAAM,GAAG,SAAS,MAAM,UAAU,IAAI,GAAG,SAAS,CAAC,UAAU,MAAM,mBACpG,UAAA,QAAQ,IAAI,CAAC,WACV;AAAA,MAAC;AAAA,MAAA;AAAA,QAA0B,UAAU,OAAO;AAAA,QAClC,SAAS,MAAM;AACX,oBAAU,IAAI;AACd,iBAAO,QAAA;AAAA,QACX;AAAA,QACA,IAAI,OAAO,QAAQ,EAAC,OAAO,GAAG,OAAO,KAAK,QAAA,IAAW;AAAA,QAC1D,UAAA;AAAA,UAAA,OAAO,QAAQ,oBAAC,cAAA,EAAa,IAAI,OAAO,QAAQ,EAAC,OAAO,UAAA,IAAa,QAAY,UAAA,OAAO,MAAK;AAAA,UAC9F,oBAAC,cAAA,EAAc,UAAA,OAAO,MAAA,CAAM;AAAA,QAAA;AAAA,MAAA;AAAA,MAPjB,OAAO;AAAA,IAAA,CASzB,EAAA,CACL;AAAA,EAAA,GACJ;AAER;"}
@@ -7,8 +7,8 @@ import Tooltip from "@mui/material/Tooltip";
7
7
  import CheckIcon from "@mui/icons-material/CheckRounded";
8
8
  import CopyIcon from "@mui/icons-material/ContentCopyOutlined";
9
9
  const COPIED_FEEDBACK_MILLISECONDS = 2e3;
10
- function CopyButton({ value, label, size = "small", onCopied }) {
11
- const { t } = useToolkitTranslation("common");
10
+ function CopyButton({ value, label, size = "small", onCopied, texts }) {
11
+ const { t } = useToolkitTranslation("common", texts);
12
12
  const [isCopied, setIsCopied] = useState(false);
13
13
  const buttonLabel = label ?? t("form.copy");
14
14
  const copy = async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"CopyButton.js","sources":["../../../src/ui/actions/CopyButton.jsx"],"sourcesContent":["// Botón para copiar un valor al portapapeles, con la palomita de «copiado» un par de segundos.\n//\n// Sin permiso de portapapeles (una página en http, dentro de un iframe) no truena: el valor sigue a la vista para copiarlo\n// a mano.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport CheckIcon from '@mui/icons-material/CheckRounded';\nimport CopyIcon from '@mui/icons-material/ContentCopyOutlined';\n\n// cuánto se queda la palomita de «copiado»\nconst COPIED_FEEDBACK_MILLISECONDS = 2000;\n\n/**\n * @param {object} props\n * @param {string} props.value lo que se copia\n * @param {string} [props.label] ya traducido; por omisión «Copiar»\n * @param {'small'|'medium'} [props.size]\n * @param {() => void} [props.onCopied]\n */\nexport default function CopyButton({value, label, size = 'small', onCopied}) {\n const {t} = useToolkitTranslation('common');\n const [isCopied, setIsCopied] = useState(false);\n const buttonLabel = label ?? t('form.copy');\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setIsCopied(true);\n onCopied?.();\n setTimeout(() => setIsCopied(false), COPIED_FEEDBACK_MILLISECONDS);\n } catch {\n // sin permiso de portapapeles: el valor está a la vista para copiarlo a mano\n }\n };\n\n return (\n <Tooltip title={isCopied ? t('form.copied') : buttonLabel}>\n <IconButton size={size} aria-label={buttonLabel} onClick={copy} color={isCopied ? 'success' : 'default'}>\n {isCopied ? <CheckIcon fontSize=\"inherit\" /> : <CopyIcon fontSize=\"inherit\" />}\n </IconButton>\n </Tooltip>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAcA,MAAM,+BAA+B;AASrC,SAAwB,WAAW,EAAC,OAAO,OAAO,OAAO,SAAS,YAAW;AACzE,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,cAAc,SAAS,EAAE,WAAW;AAE1C,QAAM,OAAO,YAAY;AACrB,QAAI;AACA,YAAM,UAAU,UAAU,UAAU,KAAK;AACzC,kBAAY,IAAI;AAChB,iBAAA;AACA,iBAAW,MAAM,YAAY,KAAK,GAAG,4BAA4B;AAAA,IACrE,QAAQ;AAAA,IAER;AAAA,EACJ;AAEA,SACI,oBAAC,SAAA,EAAQ,OAAO,WAAW,EAAE,aAAa,IAAI,aAC1C,UAAA,oBAAC,YAAA,EAAW,MAAY,cAAY,aAAa,SAAS,MAAM,OAAO,WAAW,YAAY,WACzF,UAAA,WAAW,oBAAC,WAAA,EAAU,UAAS,UAAA,CAAU,IAAK,oBAAC,UAAA,EAAS,UAAS,UAAA,CAAU,GAChF,GACJ;AAER;"}
1
+ {"version":3,"file":"CopyButton.js","sources":["../../../src/ui/actions/CopyButton.jsx"],"sourcesContent":["// Botón para copiar un valor al portapapeles, con la palomita de «copiado» un par de segundos.\n//\n// Sin permiso de portapapeles (una página en http, dentro de un iframe) no truena: el valor sigue a la vista para copiarlo\n// a mano.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport CheckIcon from '@mui/icons-material/CheckRounded';\nimport CopyIcon from '@mui/icons-material/ContentCopyOutlined';\n\n// cuánto se queda la palomita de «copiado»\nconst COPIED_FEEDBACK_MILLISECONDS = 2000;\n\n/**\n * @param {object} props\n * @param {string} props.value lo que se copia\n * @param {string} [props.label] ya traducido; por omisión «Copiar»\n * @param {'small'|'medium'} [props.size]\n * @param {() => void} [props.onCopied]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function CopyButton({value, label, size = 'small', onCopied, texts}) {\n const {t} = useToolkitTranslation('common', texts);\n const [isCopied, setIsCopied] = useState(false);\n const buttonLabel = label ?? t('form.copy');\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setIsCopied(true);\n onCopied?.();\n setTimeout(() => setIsCopied(false), COPIED_FEEDBACK_MILLISECONDS);\n } catch {\n // sin permiso de portapapeles: el valor está a la vista para copiarlo a mano\n }\n };\n\n return (\n <Tooltip title={isCopied ? t('form.copied') : buttonLabel}>\n <IconButton size={size} aria-label={buttonLabel} onClick={copy} color={isCopied ? 'success' : 'default'}>\n {isCopied ? <CheckIcon fontSize=\"inherit\" /> : <CopyIcon fontSize=\"inherit\" />}\n </IconButton>\n </Tooltip>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAcA,MAAM,+BAA+B;AAUrC,SAAwB,WAAW,EAAC,OAAO,OAAO,OAAO,SAAS,UAAU,SAAQ;AAChF,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,cAAc,SAAS,EAAE,WAAW;AAE1C,QAAM,OAAO,YAAY;AACrB,QAAI;AACA,YAAM,UAAU,UAAU,UAAU,KAAK;AACzC,kBAAY,IAAI;AAChB,iBAAA;AACA,iBAAW,MAAM,YAAY,KAAK,GAAG,4BAA4B;AAAA,IACrE,QAAQ;AAAA,IAER;AAAA,EACJ;AAEA,SACI,oBAAC,SAAA,EAAQ,OAAO,WAAW,EAAE,aAAa,IAAI,aAC1C,UAAA,oBAAC,YAAA,EAAW,MAAY,cAAY,aAAa,SAAS,MAAM,OAAO,WAAW,YAAY,WACzF,UAAA,WAAW,oBAAC,WAAA,EAAU,UAAS,UAAA,CAAU,IAAK,oBAAC,UAAA,EAAS,UAAS,UAAA,CAAU,GAChF,GACJ;AAER;"}
@@ -11,18 +11,11 @@ import MenuItem from "@mui/material/MenuItem";
11
11
  import Tooltip from "@mui/material/Tooltip";
12
12
  import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
13
13
  import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
14
- import PictureAsPdfOutlinedIcon from "@mui/icons-material/PictureAsPdfOutlined";
15
- import TableViewOutlinedIcon from "@mui/icons-material/TableViewOutlined";
16
- import TextSnippetOutlinedIcon from "@mui/icons-material/TextSnippetOutlined";
17
14
  import { useBarActionsPlacement, useCloseBarActionsMenu, BAR_ACTIONS_PLACEMENT } from "../../appShell/barActionsSlot.js";
18
15
  import { startDownload } from "../../downloads/downloadQueue.js";
16
+ import { useFileTypes } from "../../downloads/fileTypes.js";
19
17
  import ConfirmDialog from "../../feedback/ConfirmDialog.js";
20
18
  import { useConfirmAction } from "../../feedback/useConfirmAction.js";
21
- const ICON_BY_FORMAT = {
22
- pdf: PictureAsPdfOutlinedIcon,
23
- xlsx: TableViewOutlinedIcon,
24
- csv: TextSnippetOutlinedIcon
25
- };
26
19
  function DownloadMenu({
27
20
  formats,
28
21
  download,
@@ -31,12 +24,15 @@ function DownloadMenu({
31
24
  disabled = false,
32
25
  confirmation,
33
26
  onDownloaded,
27
+ onFailed,
28
+ onCanceled,
34
29
  variant = "button",
35
30
  ariaLabel,
36
31
  onChooseFormat,
37
32
  icon: ButtonIcon = FileDownloadOutlinedIcon
38
33
  }) {
39
34
  const { t } = useToolkitTranslation("common");
35
+ const fileTypes = useFileTypes();
40
36
  const [anchorElement, setAnchorElement] = useState(null);
41
37
  const [anchorPosition, setAnchorPosition] = useState(null);
42
38
  const placement = useBarActionsPlacement();
@@ -50,7 +46,9 @@ function DownloadMenu({
50
46
  label: downloadLabel ?? label ?? ariaLabel ?? t("actions.download"),
51
47
  format,
52
48
  request: (transferOptions) => download(format, transferOptions),
53
- onCompleted: () => onDownloaded?.(format)
49
+ onCompleted: () => onDownloaded?.(format),
50
+ onFailed: (error) => onFailed?.(format, error),
51
+ onCanceled: () => onCanceled?.(format)
54
52
  });
55
53
  };
56
54
  const confirmedDownload = useConfirmAction({ run: ({ subject }) => runDownload(subject.format) });
@@ -131,14 +129,15 @@ function DownloadMenu({
131
129
  anchorOrigin: { vertical: "bottom", horizontal: "right" },
132
130
  transformOrigin: { vertical: "top", horizontal: "right" },
133
131
  children: formats.map((format) => {
134
- const Icon = ICON_BY_FORMAT[format] ?? FileDownloadOutlinedIcon;
132
+ const fileType = fileTypes[format];
133
+ const Icon = fileType?.Icon ?? FileDownloadOutlinedIcon;
135
134
  return /* @__PURE__ */ jsxs(MenuItem, { onClick: () => chooseFormat(format), children: [
136
135
  /* @__PURE__ */ jsx(ListItemIcon, { children: /* @__PURE__ */ jsx(Icon, { fontSize: "small" }) }),
137
136
  /* @__PURE__ */ jsx(
138
137
  ListItemText,
139
138
  {
140
- primary: t(`download.formats.${format}.label`),
141
- secondary: t(`download.formats.${format}.hint`),
139
+ primary: t(`download.formats.${format}.label`, { defaultValue: fileType?.label ?? format.toUpperCase() }),
140
+ secondary: t(`download.formats.${format}.hint`, { defaultValue: fileType?.hint ?? "" }),
142
141
  slotProps: { secondary: { variant: "caption" } }
143
142
  }
144
143
  )
@@ -1 +1 @@
1
- {"version":3,"file":"DownloadMenu.js","sources":["../../../src/ui/actions/DownloadMenu.jsx"],"sourcesContent":["// \"Descargar ▾\" con los formatos de un reporte: PDF para leer o archivar, Excel o CSV para trabajar.\n//\n// Un solo botón y no tres: en la barra de una pantalla tres botones de descarga compiten con la acción principal.\n// Se acomoda a donde quedó, como BarActionButton: en una cápsula de la barra (las `actions` de PageHeading con cápsulas)\n// es solo el ícono con su nombre en el tooltip, como las demás acciones; antes salía la píldora «Descargar ▾» dentro de\n// la cápsula y se veía una cápsula dentro de otra. En el «⋯» del teléfono es un renglón que abre los formatos.\n// Quien llama pasa QUÉ descargar por formato (download(format, transferOptions) → {blob, fileName}, la forma de\n// un cliente HTTP que descarga). La descarga va al panel de descargas (startDownload, downloads): el diálogo se cierra al\n// confirmar y el avance, el aviso de \"descargado\" y el error se ven allá, mientras la persona sigue trabajando.\n//\n// Regla de tablas: una descarga pasa por un diálogo de confirmación que dice qué se va a bajar (p. ej. que la base\n// completa de clientes tarda unos minutos). Con `confirmation` el formato elegido NO se descarga de inmediato: abre\n// ConfirmDialog (máquina confirmActionMachine) y solo al confirmar se pide el archivo, una sola vez aunque haya doble clic.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\nimport ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';\nimport FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';\nimport PictureAsPdfOutlinedIcon from '@mui/icons-material/PictureAsPdfOutlined';\nimport TableViewOutlinedIcon from '@mui/icons-material/TableViewOutlined';\nimport TextSnippetOutlinedIcon from '@mui/icons-material/TextSnippetOutlined';\n\nimport {BAR_ACTIONS_PLACEMENT, useBarActionsPlacement, useCloseBarActionsMenu} from '@/appShell/barActionsSlot.js';\nimport {startDownload} from '@/downloads/downloadQueue.js';\nimport ConfirmDialog from '@/feedback/ConfirmDialog.jsx';\nimport {useConfirmAction} from '@/feedback/useConfirmAction.js';\n\nconst ICON_BY_FORMAT = {\n pdf: PictureAsPdfOutlinedIcon,\n xlsx: TableViewOutlinedIcon,\n csv: TextSnippetOutlinedIcon,\n};\n\n/**\n * @param {object} props\n * @param {Array<'pdf'|'xlsx'|'csv'>} props.formats\n * @param {(format: string, transferOptions: {signal: AbortSignal, onDownloadProgress: (progress: {loaded: number, total?: number|null}) => void}) => Promise<{blob: Blob, fileName: string}>} props.download\n * @param {string} [props.label] ya traducido; por defecto \"Descargar\"\n * @param {string} [props.downloadLabel] ya traducido; lo que se ve en el panel de descargas antes de saber el nombre\n * del archivo\n * @param {boolean} [props.disabled]\n * @param {(format: string) => {title: string, description: import('react').ReactNode, confirmLabel?: string}} [props.confirmation]\n * @param {(format: string) => void} [props.onDownloaded] p. ej. volver a pedir la tabla (si la descarga cambia algo en\n * el servidor)\n * @param {'button'|'icon'} [props.variant] icon = solo el icono (columna de acciones de una tabla)\n * @param {string} [props.ariaLabel] ya traducido; obligatorio en la variante icon\n * @param {(format: string) => void} [props.onChooseFormat] quien llama se encarga de confirmar y descargar\n * @param {import('react').ElementType} [props.icon] ícono del botón; por defecto el de descarga. Distingue dos descargas en la\n * misma fila\n *\n * IMPORTANTE: hay que pasar `confirmation` u `onChooseFormat`. Sin ninguno de los dos el menú no descarga nada (regla de\n * tablas: toda descarga pasa por confirmación).\n *\n * OJO onChooseFormat: dentro de una celda del DataGrid el diálogo de confirmación NO puede vivir en la celda (el grid\n * virtualiza y vuelve a pintar sus celdas; el diálogo se perdía al elegir el formato). Ahí el menú solo avisa qué formato\n * se eligió y la pantalla abre SU confirmación (useConfirmAction) fuera de la tabla.\n */\nexport default function DownloadMenu({\n formats, download, label, downloadLabel, disabled = false, confirmation, onDownloaded, variant = 'button', ariaLabel, onChooseFormat,\n icon: ButtonIcon = FileDownloadOutlinedIcon,\n}) {\n const {t} = useToolkitTranslation('common');\n // de dónde sale el menú de formatos: del botón, o (desde el «⋯» del teléfono, que se cierra) de donde estaba el renglón\n const [anchorElement, setAnchorElement] = useState(null);\n const [anchorPosition, setAnchorPosition] = useState(null);\n const placement = useBarActionsPlacement();\n const closeMoreMenu = useCloseBarActionsMenu();\n const closeFormats = () => {\n setAnchorElement(null);\n setAnchorPosition(null);\n };\n\n // no se espera a que termine: la descarga sigue en el panel y el diálogo de confirmación se cierra de inmediato\n const runDownload = (format) => {\n startDownload({\n label: downloadLabel ?? label ?? ariaLabel ?? t('actions.download'),\n format,\n request: (transferOptions) => download(format, transferOptions),\n onCompleted: () => onDownloaded?.(format),\n });\n };\n\n const confirmedDownload = useConfirmAction({run: ({subject}) => runDownload(subject.format)});\n\n const chooseFormat = (format) => {\n closeFormats();\n if (onChooseFormat) {\n onChooseFormat(format);\n return;\n }\n if (confirmation) {\n confirmedDownload.open({format});\n return;\n }\n // Regla de tablas: nada se descarga con un solo clic. Sin confirmation ni onChooseFormat es un error de quien\n // llama, no un camino válido: lo decimos en la consola y no descargamos\n console.error('DownloadMenu: falta confirmation u onChooseFormat; toda descarga pasa por un diálogo de confirmación');\n };\n\n const buttonLabel = label ?? t('actions.download');\n const openMenu = (event) => setAnchorElement(event.currentTarget);\n // desde el «⋯»: se cierra ese menú y los formatos salen donde estaba el renglón\n const openFromMoreMenu = (event) => {\n const {bottom, right} = event.currentTarget.getBoundingClientRect();\n closeMoreMenu();\n setAnchorPosition({top: bottom, left: right});\n };\n const isFormatsOpen = Boolean(anchorElement || anchorPosition);\n const confirmationTexts = confirmedDownload.subject ? confirmation?.(confirmedDownload.subject.format) : null;\n\n return (\n <>\n {placement === BAR_ACTIONS_PLACEMENT.menu && (\n <MenuItem disabled={disabled} aria-haspopup=\"menu\" onClick={openFromMoreMenu}>\n <ListItemIcon><ButtonIcon /></ListItemIcon>\n <ListItemText>{buttonLabel}</ListItemText>\n </MenuItem>\n )}\n {placement === BAR_ACTIONS_PLACEMENT.capsule && (\n // en la cápsula, del tamaño y color de las demás acciones (BarActionButton)\n <Tooltip title={buttonLabel} describeChild>\n <span>\n <IconButton color=\"primary\" aria-label={buttonLabel} aria-haspopup=\"menu\" aria-expanded={isFormatsOpen}\n disabled={disabled} onClick={openMenu}>\n <ButtonIcon />\n </IconButton>\n </span>\n </Tooltip>\n )}\n {placement === BAR_ACTIONS_PLACEMENT.inline && (variant === 'icon' ? (\n // describeChild: el nombre accesible ya lo trae el botón; sin esto el Tooltip le pone el mismo aria-label al\n // span y un lector de pantalla (o una prueba) encuentra dos \"botones\" con el mismo nombre\n <Tooltip title={ariaLabel ?? buttonLabel} describeChild>\n {/* el span deja que el tooltip funcione con el botón inhabilitado */}\n <span>\n <IconButton size=\"small\" aria-label={ariaLabel ?? buttonLabel} aria-haspopup=\"menu\"\n disabled={disabled} onClick={openMenu}>\n <ButtonIcon fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n ) : (\n <Button variant=\"outlined\" startIcon={<ButtonIcon />} endIcon={<ArrowDropDownIcon />}\n sx={{flexShrink: 0}} disabled={disabled} aria-haspopup=\"menu\" onClick={openMenu}>\n {buttonLabel}\n </Button>\n ))}\n <Menu open={isFormatsOpen} onClose={closeFormats}\n anchorReference={anchorPosition ? 'anchorPosition' : 'anchorEl'} anchorEl={anchorElement} anchorPosition={anchorPosition ?? undefined}\n anchorOrigin={{vertical: 'bottom', horizontal: 'right'}} transformOrigin={{vertical: 'top', horizontal: 'right'}}>\n {formats.map((format) => {\n const Icon = ICON_BY_FORMAT[format] ?? FileDownloadOutlinedIcon;\n return (\n <MenuItem key={format} onClick={() => chooseFormat(format)}>\n <ListItemIcon><Icon fontSize=\"small\" /></ListItemIcon>\n <ListItemText primary={t(`download.formats.${format}.label`)} secondary={t(`download.formats.${format}.hint`)}\n slotProps={{secondary: {variant: 'caption'}}} />\n </MenuItem>\n );\n })}\n </Menu>\n {confirmation && (\n <ConfirmDialog\n {...confirmedDownload.dialogProps}\n title={confirmationTexts?.title ?? ''}\n description={confirmationTexts?.description}\n confirmLabel={confirmationTexts?.confirmLabel ?? t('actions.download')}\n />\n )}\n </>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DownloadMenu.js","sources":["../../../src/ui/actions/DownloadMenu.jsx"],"sourcesContent":["// \"Descargar ▾\" con los formatos de un reporte: PDF para leer o archivar, Excel o CSV para trabajar.\n//\n// Un solo botón y no tres: en la barra de una pantalla tres botones de descarga compiten con la acción principal.\n// Se acomoda a donde quedó, como BarActionButton: en una cápsula de la barra (las `actions` de PageHeading con cápsulas)\n// es solo el ícono con su nombre en el tooltip, como las demás acciones; antes salía la píldora «Descargar ▾» dentro de\n// la cápsula y se veía una cápsula dentro de otra. En el «⋯» del teléfono es un renglón que abre los formatos.\n// Quien llama pasa QUÉ descargar por formato (download(format, transferOptions) → {blob, fileName}, la forma de\n// un cliente HTTP que descarga). La descarga va al panel de descargas (startDownload, downloads): el diálogo se cierra al\n// confirmar y el avance, el aviso de \"descargado\" y el error se ven allá, mientras la persona sigue trabajando.\n//\n// Regla de tablas: una descarga pasa por un diálogo de confirmación que dice qué se va a bajar (p. ej. que la base\n// completa de clientes tarda unos minutos). Con `confirmation` el formato elegido NO se descarga de inmediato: abre\n// ConfirmDialog (máquina confirmActionMachine) y solo al confirmar se pide el archivo, una sola vez aunque haya doble clic.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\nimport ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';\nimport FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';\n\nimport {BAR_ACTIONS_PLACEMENT, useBarActionsPlacement, useCloseBarActionsMenu} from '@/appShell/barActionsSlot.js';\nimport {startDownload} from '@/downloads/downloadQueue.js';\nimport {useFileTypes} from '@/downloads/fileTypes.js';\nimport ConfirmDialog from '@/feedback/ConfirmDialog.jsx';\nimport {useConfirmAction} from '@/feedback/useConfirmAction.js';\n\n\n/**\n * @param {object} props\n * @param {string[]} props.formats los tipos de archivo, en orden: los de la casa (xlsx, csv, pdf) y los que el proyecto declare\n * en ToolkitProvider fileTypes (zip, xml…)\n * @param {(format: string, transferOptions: {signal: AbortSignal, onDownloadProgress: (progress: {loaded: number, total?: number|null}) => void}) => Promise<{blob: Blob, fileName: string}>} props.download\n * @param {string} [props.label] ya traducido; por defecto \"Descargar\"\n * @param {string} [props.downloadLabel] ya traducido; lo que se ve en el panel de descargas antes de saber el nombre\n * del archivo\n * @param {boolean} [props.disabled]\n * @param {(format: string) => {title: string, description: import('react').ReactNode, confirmLabel?: string}} [props.confirmation]\n * @param {(format: string) => void} [props.onDownloaded] p. ej. volver a pedir la tabla (si la descarga cambia algo en\n * el servidor)\n * @param {(format: string, error: unknown) => void} [props.onFailed] cuando la descarga no se pudo (el panel ya lo dice)\n * @param {(format: string) => void} [props.onCanceled] cuando la persona la canceló desde el panel\n * @param {'button'|'icon'} [props.variant] icon = solo el icono (columna de acciones de una tabla)\n * @param {string} [props.ariaLabel] ya traducido; obligatorio en la variante icon\n * @param {(format: string) => void} [props.onChooseFormat] quien llama se encarga de confirmar y descargar\n * @param {import('react').ElementType} [props.icon] ícono del botón; por defecto el de descarga. Distingue dos descargas en la\n * misma fila\n *\n * IMPORTANTE: hay que pasar `confirmation` u `onChooseFormat`. Sin ninguno de los dos el menú no descarga nada (regla de\n * tablas: toda descarga pasa por confirmación).\n *\n * OJO onChooseFormat: dentro de una celda del DataGrid el diálogo de confirmación NO puede vivir en la celda (el grid\n * virtualiza y vuelve a pintar sus celdas; el diálogo se perdía al elegir el formato). Ahí el menú solo avisa qué formato\n * se eligió y la pantalla abre SU confirmación (useConfirmAction) fuera de la tabla.\n */\nexport default function DownloadMenu({\n formats, download, label, downloadLabel, disabled = false, confirmation, onDownloaded, onFailed, onCanceled, variant = 'button', ariaLabel,\n onChooseFormat, icon: ButtonIcon = FileDownloadOutlinedIcon,\n}) {\n const {t} = useToolkitTranslation('common');\n const fileTypes = useFileTypes();\n // de dónde sale el menú de formatos: del botón, o (desde el «⋯» del teléfono, que se cierra) de donde estaba el renglón\n const [anchorElement, setAnchorElement] = useState(null);\n const [anchorPosition, setAnchorPosition] = useState(null);\n const placement = useBarActionsPlacement();\n const closeMoreMenu = useCloseBarActionsMenu();\n const closeFormats = () => {\n setAnchorElement(null);\n setAnchorPosition(null);\n };\n\n // no se espera a que termine: la descarga sigue en el panel y el diálogo de confirmación se cierra de inmediato\n const runDownload = (format) => {\n startDownload({\n label: downloadLabel ?? label ?? ariaLabel ?? t('actions.download'),\n format,\n request: (transferOptions) => download(format, transferOptions),\n onCompleted: () => onDownloaded?.(format),\n onFailed: (error) => onFailed?.(format, error),\n onCanceled: () => onCanceled?.(format),\n });\n };\n\n const confirmedDownload = useConfirmAction({run: ({subject}) => runDownload(subject.format)});\n\n const chooseFormat = (format) => {\n closeFormats();\n if (onChooseFormat) {\n onChooseFormat(format);\n return;\n }\n if (confirmation) {\n confirmedDownload.open({format});\n return;\n }\n // Regla de tablas: nada se descarga con un solo clic. Sin confirmation ni onChooseFormat es un error de quien\n // llama, no un camino válido: lo decimos en la consola y no descargamos\n console.error('DownloadMenu: falta confirmation u onChooseFormat; toda descarga pasa por un diálogo de confirmación');\n };\n\n const buttonLabel = label ?? t('actions.download');\n const openMenu = (event) => setAnchorElement(event.currentTarget);\n // desde el «⋯»: se cierra ese menú y los formatos salen donde estaba el renglón\n const openFromMoreMenu = (event) => {\n const {bottom, right} = event.currentTarget.getBoundingClientRect();\n closeMoreMenu();\n setAnchorPosition({top: bottom, left: right});\n };\n const isFormatsOpen = Boolean(anchorElement || anchorPosition);\n const confirmationTexts = confirmedDownload.subject ? confirmation?.(confirmedDownload.subject.format) : null;\n\n return (\n <>\n {placement === BAR_ACTIONS_PLACEMENT.menu && (\n <MenuItem disabled={disabled} aria-haspopup=\"menu\" onClick={openFromMoreMenu}>\n <ListItemIcon><ButtonIcon /></ListItemIcon>\n <ListItemText>{buttonLabel}</ListItemText>\n </MenuItem>\n )}\n {placement === BAR_ACTIONS_PLACEMENT.capsule && (\n // en la cápsula, del tamaño y color de las demás acciones (BarActionButton)\n <Tooltip title={buttonLabel} describeChild>\n <span>\n <IconButton color=\"primary\" aria-label={buttonLabel} aria-haspopup=\"menu\" aria-expanded={isFormatsOpen}\n disabled={disabled} onClick={openMenu}>\n <ButtonIcon />\n </IconButton>\n </span>\n </Tooltip>\n )}\n {placement === BAR_ACTIONS_PLACEMENT.inline && (variant === 'icon' ? (\n // describeChild: el nombre accesible ya lo trae el botón; sin esto el Tooltip le pone el mismo aria-label al\n // span y un lector de pantalla (o una prueba) encuentra dos \"botones\" con el mismo nombre\n <Tooltip title={ariaLabel ?? buttonLabel} describeChild>\n {/* el span deja que el tooltip funcione con el botón inhabilitado */}\n <span>\n <IconButton size=\"small\" aria-label={ariaLabel ?? buttonLabel} aria-haspopup=\"menu\"\n disabled={disabled} onClick={openMenu}>\n <ButtonIcon fontSize=\"small\" />\n </IconButton>\n </span>\n </Tooltip>\n ) : (\n <Button variant=\"outlined\" startIcon={<ButtonIcon />} endIcon={<ArrowDropDownIcon />}\n sx={{flexShrink: 0}} disabled={disabled} aria-haspopup=\"menu\" onClick={openMenu}>\n {buttonLabel}\n </Button>\n ))}\n <Menu open={isFormatsOpen} onClose={closeFormats}\n anchorReference={anchorPosition ? 'anchorPosition' : 'anchorEl'} anchorEl={anchorElement} anchorPosition={anchorPosition ?? undefined}\n anchorOrigin={{vertical: 'bottom', horizontal: 'right'}} transformOrigin={{vertical: 'top', horizontal: 'right'}}>\n {formats.map((format) => {\n const fileType = fileTypes[format];\n const Icon = fileType?.Icon ?? FileDownloadOutlinedIcon;\n return (\n <MenuItem key={format} onClick={() => chooseFormat(format)}>\n <ListItemIcon><Icon fontSize=\"small\" /></ListItemIcon>\n {/* los de la casa con sus textos; los del proyecto con su label y hint (o esas llaves en sus texts) */}\n <ListItemText primary={t(`download.formats.${format}.label`, {defaultValue: fileType?.label ?? format.toUpperCase()})}\n secondary={t(`download.formats.${format}.hint`, {defaultValue: fileType?.hint ?? ''})}\n slotProps={{secondary: {variant: 'caption'}}} />\n </MenuItem>\n );\n })}\n </Menu>\n {confirmation && (\n <ConfirmDialog\n {...confirmedDownload.dialogProps}\n title={confirmationTexts?.title ?? ''}\n description={confirmationTexts?.description}\n confirmLabel={confirmationTexts?.confirmLabel ?? t('actions.download')}\n />\n )}\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA6DA,SAAwB,aAAa;AAAA,EACjC;AAAA,EAAS;AAAA,EAAU;AAAA,EAAO;AAAA,EAAe,WAAW;AAAA,EAAO;AAAA,EAAc;AAAA,EAAc;AAAA,EAAU;AAAA,EAAY,UAAU;AAAA,EAAU;AAAA,EACjI;AAAA,EAAgB,MAAM,aAAa;AACvC,GAAG;AACC,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,YAAY,aAAA;AAElB,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AACvD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,IAAI;AACzD,QAAM,YAAY,uBAAA;AAClB,QAAM,gBAAgB,uBAAA;AACtB,QAAM,eAAe,MAAM;AACvB,qBAAiB,IAAI;AACrB,sBAAkB,IAAI;AAAA,EAC1B;AAGA,QAAM,cAAc,CAAC,WAAW;AAC5B,kBAAc;AAAA,MACV,OAAO,iBAAiB,SAAS,aAAa,EAAE,kBAAkB;AAAA,MAClE;AAAA,MACA,SAAS,CAAC,oBAAoB,SAAS,QAAQ,eAAe;AAAA,MAC9D,aAAa,MAAM,eAAe,MAAM;AAAA,MACxC,UAAU,CAAC,UAAU,WAAW,QAAQ,KAAK;AAAA,MAC7C,YAAY,MAAM,aAAa,MAAM;AAAA,IAAA,CACxC;AAAA,EACL;AAEA,QAAM,oBAAoB,iBAAiB,EAAC,KAAK,CAAC,EAAC,QAAA,MAAa,YAAY,QAAQ,MAAM,GAAE;AAE5F,QAAM,eAAe,CAAC,WAAW;AAC7B,iBAAA;AACA,QAAI,gBAAgB;AAChB,qBAAe,MAAM;AACrB;AAAA,IACJ;AACA,QAAI,cAAc;AACd,wBAAkB,KAAK,EAAC,QAAO;AAC/B;AAAA,IACJ;AAGA,YAAQ,MAAM,sGAAsG;AAAA,EACxH;AAEA,QAAM,cAAc,SAAS,EAAE,kBAAkB;AACjD,QAAM,WAAW,CAAC,UAAU,iBAAiB,MAAM,aAAa;AAEhE,QAAM,mBAAmB,CAAC,UAAU;AAChC,UAAM,EAAC,QAAQ,MAAA,IAAS,MAAM,cAAc,sBAAA;AAC5C,kBAAA;AACA,sBAAkB,EAAC,KAAK,QAAQ,MAAM,OAAM;AAAA,EAChD;AACA,QAAM,gBAAgB,QAAQ,iBAAiB,cAAc;AAC7D,QAAM,oBAAoB,kBAAkB,UAAU,eAAe,kBAAkB,QAAQ,MAAM,IAAI;AAEzG,SACI,qBAAA,UAAA,EACK,UAAA;AAAA,IAAA,cAAc,sBAAsB,QACjC,qBAAC,UAAA,EAAS,UAAoB,iBAAc,QAAO,SAAS,kBACxD,UAAA;AAAA,MAAA,oBAAC,cAAA,EAAa,UAAA,oBAAC,YAAA,CAAA,CAAW,GAAE;AAAA,MAC5B,oBAAC,gBAAc,UAAA,YAAA,CAAY;AAAA,IAAA,GAC/B;AAAA,IAEH,cAAc,sBAAsB;AAAA,wBAEhC,SAAA,EAAQ,OAAO,aAAa,eAAa,MACtC,8BAAC,QAAA,EACG,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAW,OAAM;AAAA,QAAU,cAAY;AAAA,QAAa,iBAAc;AAAA,QAAO,iBAAe;AAAA,QAC7E;AAAA,QAAoB,SAAS;AAAA,QACrC,8BAAC,YAAA,CAAA,CAAW;AAAA,MAAA;AAAA,IAAA,GAEpB,EAAA,CACJ;AAAA,IAEH,cAAc,sBAAsB,WAAW,YAAY;AAAA;AAAA;AAAA,MAGxD,oBAAC,WAAQ,OAAO,aAAa,aAAa,eAAa,MAEnD,8BAAC,QAAA,EACG,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UAAW,MAAK;AAAA,UAAQ,cAAY,aAAa;AAAA,UAAa,iBAAc;AAAA,UACjE;AAAA,UAAoB,SAAS;AAAA,UACrC,UAAA,oBAAC,YAAA,EAAW,UAAS,QAAA,CAAQ;AAAA,QAAA;AAAA,MAAA,GAErC,EAAA,CACJ;AAAA,QAEA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAO,SAAQ;AAAA,QAAW,+BAAY,YAAA,EAAW;AAAA,QAAI,6BAAU,mBAAA,EAAkB;AAAA,QAC1E,IAAI,EAAC,YAAY,EAAA;AAAA,QAAI;AAAA,QAAoB,iBAAc;AAAA,QAAO,SAAS;AAAA,QAC1E,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAGT;AAAA,MAAC;AAAA,MAAA;AAAA,QAAK,MAAM;AAAA,QAAe,SAAS;AAAA,QAC9B,iBAAiB,iBAAiB,mBAAmB;AAAA,QAAY,UAAU;AAAA,QAAe,gBAAgB,kBAAkB;AAAA,QAC5H,cAAc,EAAC,UAAU,UAAU,YAAY,QAAA;AAAA,QAAU,iBAAiB,EAAC,UAAU,OAAO,YAAY,QAAA;AAAA,QACzG,UAAA,QAAQ,IAAI,CAAC,WAAW;AACrB,gBAAM,WAAW,UAAU,MAAM;AACjC,gBAAM,OAAO,UAAU,QAAQ;AAC/B,sCACK,UAAA,EAAsB,SAAS,MAAM,aAAa,MAAM,GACrD,UAAA;AAAA,YAAA,oBAAC,cAAA,EAAa,UAAA,oBAAC,MAAA,EAAK,UAAS,SAAQ,GAAE;AAAA,YAEvC;AAAA,cAAC;AAAA,cAAA;AAAA,gBAAa,SAAS,EAAE,oBAAoB,MAAM,UAAU,EAAC,cAAc,UAAU,SAAS,OAAO,YAAA,EAAY,CAAE;AAAA,gBACtG,WAAW,EAAE,oBAAoB,MAAM,SAAS,EAAC,cAAc,UAAU,QAAQ,GAAA,CAAG;AAAA,gBACpF,WAAW,EAAC,WAAW,EAAC,SAAS,YAAS;AAAA,cAAC;AAAA,YAAA;AAAA,UAAG,EAAA,GALjD,MAMf;AAAA,QAER,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,IAEJ,gBACG;AAAA,MAAC;AAAA,MAAA;AAAA,QACI,GAAG,kBAAkB;AAAA,QACtB,OAAO,mBAAmB,SAAS;AAAA,QACnC,aAAa,mBAAmB;AAAA,QAChC,cAAc,mBAAmB,gBAAgB,EAAE,kBAAkB;AAAA,MAAA;AAAA,IAAA;AAAA,EACzE,GAER;AAER;"}
@@ -13,11 +13,15 @@ import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined";
13
13
  import FieldFrame from "../../inputs/FieldFrame.js";
14
14
  const COPIED_FEEDBACK_MILLISECONDS = 2e3;
15
15
  const MONOSPACE_FONT = "ui-monospace, SFMono-Regular, Menlo, monospace";
16
- function SecretField({ value, label, helperText = " ", onCopied }) {
17
- const { t } = useToolkitTranslation("common");
16
+ function SecretField({ value, label, helperText = " ", onCopied, onRevealChange, texts }) {
17
+ const { t } = useToolkitTranslation("common", texts);
18
18
  const generatedId = useId();
19
19
  const inputId = `secret-${generatedId}`;
20
- const [isRevealed, setIsRevealed] = useState(false);
20
+ const [isRevealed, setIsRevealedState] = useState(false);
21
+ const setIsRevealed = (nextIsRevealed) => {
22
+ setIsRevealedState(nextIsRevealed);
23
+ onRevealChange?.(nextIsRevealed);
24
+ };
21
25
  const [isCopied, setIsCopied] = useState(false);
22
26
  const revealLabel = isRevealed ? t("form.hidePassword") : t("form.showPassword");
23
27
  const copy = async () => {
@@ -43,7 +47,7 @@ function SecretField({ value, label, helperText = " ", onCopied }) {
43
47
  htmlInput: { readOnly: true, spellCheck: false, autoComplete: "off", style: { fontFamily: MONOSPACE_FONT } },
44
48
  input: {
45
49
  endAdornment: /* @__PURE__ */ jsxs(InputAdornment, { position: "end", children: [
46
- /* @__PURE__ */ jsx(Tooltip, { title: revealLabel, children: /* @__PURE__ */ jsx(IconButton, { size: "small", "aria-label": revealLabel, onClick: () => setIsRevealed((current) => !current), children: isRevealed ? /* @__PURE__ */ jsx(VisibilityOffOutlinedIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(VisibilityOutlinedIcon, { fontSize: "small" }) }) }),
50
+ /* @__PURE__ */ jsx(Tooltip, { title: revealLabel, children: /* @__PURE__ */ jsx(IconButton, { size: "small", "aria-label": revealLabel, onClick: () => setIsRevealed(!isRevealed), children: isRevealed ? /* @__PURE__ */ jsx(VisibilityOffOutlinedIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(VisibilityOutlinedIcon, { fontSize: "small" }) }) }),
47
51
  /* @__PURE__ */ jsx(Tooltip, { title: isCopied ? t("form.copied") : t("form.copy"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", "aria-label": t("form.copy"), onClick: copy, color: isCopied ? "success" : "default", children: isCopied ? /* @__PURE__ */ jsx(CheckIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(CopyIcon, { fontSize: "small" }) }) })
48
52
  ] })
49
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SecretField.js","sources":["../../../src/ui/actions/SecretField.jsx"],"sourcesContent":["// Un secreto que se enseña UNA vez (una clave de acceso, un token recién creado): tapado por omisión, con el ojo para verlo\n// y el botón para copiarlo.\n//\n// Tapado porque la pantalla se comparte, se graba o la ve quien pasa por atrás; el botón de copiar evita seleccionarlo a\n// mano y dejar un carácter afuera. Si el navegador no deja copiar, se destapa para copiarlo a mano.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport CheckIcon from '@mui/icons-material/CheckRounded';\nimport CopyIcon from '@mui/icons-material/ContentCopyOutlined';\nimport VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';\nimport VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';\n\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\n\nconst COPIED_FEEDBACK_MILLISECONDS = 2000;\nconst MONOSPACE_FONT = 'ui-monospace, SFMono-Regular, Menlo, monospace';\n\n/**\n * @param {object} props\n * @param {string} props.value el secreto\n * @param {string} props.label ya traducido\n * @param {string} [props.helperText] p. ej. «Guárdela: no se vuelve a enseñar»\n * @param {() => void} [props.onCopied]\n */\nexport default function SecretField({value, label, helperText = ' ', onCopied}) {\n const {t} = useToolkitTranslation('common');\n const generatedId = useId();\n const inputId = `secret-${generatedId}`;\n const [isRevealed, setIsRevealed] = useState(false);\n const [isCopied, setIsCopied] = useState(false);\n const revealLabel = isRevealed ? t('form.hidePassword') : t('form.showPassword');\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setIsCopied(true);\n onCopied?.();\n setTimeout(() => setIsCopied(false), COPIED_FEEDBACK_MILLISECONDS);\n } catch {\n // sin permiso de portapapeles: se destapa para copiarlo a mano\n setIsRevealed(true);\n }\n };\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <TextField\n id={inputId}\n fullWidth\n size=\"small\"\n value={value}\n helperText={helperText}\n type={isRevealed ? 'text' : 'password'}\n slotProps={{\n htmlInput: {readOnly: true, spellCheck: false, autoComplete: 'off', style: {fontFamily: MONOSPACE_FONT}},\n input: {\n endAdornment: (\n <InputAdornment position=\"end\">\n <Tooltip title={revealLabel}>\n <IconButton size=\"small\" aria-label={revealLabel} onClick={() => setIsRevealed((current) => !current)}>\n {isRevealed ? <VisibilityOffOutlinedIcon fontSize=\"small\" /> : <VisibilityOutlinedIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n <Tooltip title={isCopied ? t('form.copied') : t('form.copy')}>\n <IconButton size=\"small\" aria-label={t('form.copy')} onClick={copy} color={isCopied ? 'success' : 'default'}>\n {isCopied ? <CheckIcon fontSize=\"small\" /> : <CopyIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n </InputAdornment>\n ),\n },\n }}\n />\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAoBA,MAAM,+BAA+B;AACrC,MAAM,iBAAiB;AASvB,SAAwB,YAAY,EAAC,OAAO,OAAO,aAAa,KAAK,YAAW;AAC5E,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,UAAU,WAAW;AACrC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,cAAc,aAAa,EAAE,mBAAmB,IAAI,EAAE,mBAAmB;AAE/E,QAAM,OAAO,YAAY;AACrB,QAAI;AACA,YAAM,UAAU,UAAU,UAAU,KAAK;AACzC,kBAAY,IAAI;AAChB,iBAAA;AACA,iBAAW,MAAM,YAAY,KAAK,GAAG,4BAA4B;AAAA,IACrE,QAAQ;AAEJ,oBAAc,IAAI;AAAA,IACtB;AAAA,EACJ;AAEA,SACI,oBAAC,YAAA,EAAW,SAAkB,OAC1B,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,IAAI;AAAA,MACJ,WAAS;AAAA,MACT,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,aAAa,SAAS;AAAA,MAC5B,WAAW;AAAA,QACP,WAAW,EAAC,UAAU,MAAM,YAAY,OAAO,cAAc,OAAO,OAAO,EAAC,YAAY,eAAA,EAAc;AAAA,QACtG,OAAO;AAAA,UACH,cACI,qBAAC,gBAAA,EAAe,UAAS,OACrB,UAAA;AAAA,YAAA,oBAAC,SAAA,EAAQ,OAAO,aACZ,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,aAAa,SAAS,MAAM,cAAc,CAAC,YAAY,CAAC,OAAO,GAC/F,UAAA,aAAa,oBAAC,2BAAA,EAA0B,UAAS,QAAA,CAAQ,IAAK,oBAAC,wBAAA,EAAuB,UAAS,QAAA,CAAQ,EAAA,CAC5G,GACJ;AAAA,YACA,oBAAC,SAAA,EAAQ,OAAO,WAAW,EAAE,aAAa,IAAI,EAAE,WAAW,GACvD,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,EAAE,WAAW,GAAG,SAAS,MAAM,OAAO,WAAW,YAAY,WAC7F,qBAAW,oBAAC,WAAA,EAAU,UAAS,QAAA,CAAQ,IAAK,oBAAC,UAAA,EAAS,UAAS,QAAA,CAAQ,GAC5E,EAAA,CACJ;AAAA,UAAA,EAAA,CACJ;AAAA,QAAA;AAAA,MAER;AAAA,IACJ;AAAA,EAAA,GAER;AAER;"}
1
+ {"version":3,"file":"SecretField.js","sources":["../../../src/ui/actions/SecretField.jsx"],"sourcesContent":["// Un secreto que se enseña UNA vez (una clave de acceso, un token recién creado): tapado por omisión, con el ojo para verlo\n// y el botón para copiarlo.\n//\n// Tapado porque la pantalla se comparte, se graba o la ve quien pasa por atrás; el botón de copiar evita seleccionarlo a\n// mano y dejar un carácter afuera. Si el navegador no deja copiar, se destapa para copiarlo a mano.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport CheckIcon from '@mui/icons-material/CheckRounded';\nimport CopyIcon from '@mui/icons-material/ContentCopyOutlined';\nimport VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';\nimport VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';\n\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\n\nconst COPIED_FEEDBACK_MILLISECONDS = 2000;\nconst MONOSPACE_FONT = 'ui-monospace, SFMono-Regular, Menlo, monospace';\n\n/**\n * @param {object} props\n * @param {string} props.value el secreto\n * @param {string} props.label ya traducido\n * @param {string} [props.helperText] p. ej. «Guárdela: no se vuelve a enseñar»\n * @param {() => void} [props.onCopied]\n * @param {(isRevealed: boolean) => void} [props.onRevealChange] se destapó o se tapó (para registrar quién vio el secreto)\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SecretField({value, label, helperText = ' ', onCopied, onRevealChange, texts}) {\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `secret-${generatedId}`;\n const [isRevealed, setIsRevealedState] = useState(false);\n const setIsRevealed = (nextIsRevealed) => {\n setIsRevealedState(nextIsRevealed);\n onRevealChange?.(nextIsRevealed);\n };\n const [isCopied, setIsCopied] = useState(false);\n const revealLabel = isRevealed ? t('form.hidePassword') : t('form.showPassword');\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setIsCopied(true);\n onCopied?.();\n setTimeout(() => setIsCopied(false), COPIED_FEEDBACK_MILLISECONDS);\n } catch {\n // sin permiso de portapapeles: se destapa para copiarlo a mano\n setIsRevealed(true);\n }\n };\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <TextField\n id={inputId}\n fullWidth\n size=\"small\"\n value={value}\n helperText={helperText}\n type={isRevealed ? 'text' : 'password'}\n slotProps={{\n htmlInput: {readOnly: true, spellCheck: false, autoComplete: 'off', style: {fontFamily: MONOSPACE_FONT}},\n input: {\n endAdornment: (\n <InputAdornment position=\"end\">\n <Tooltip title={revealLabel}>\n <IconButton size=\"small\" aria-label={revealLabel} onClick={() => setIsRevealed(!isRevealed)}>\n {isRevealed ? <VisibilityOffOutlinedIcon fontSize=\"small\" /> : <VisibilityOutlinedIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n <Tooltip title={isCopied ? t('form.copied') : t('form.copy')}>\n <IconButton size=\"small\" aria-label={t('form.copy')} onClick={copy} color={isCopied ? 'success' : 'default'}>\n {isCopied ? <CheckIcon fontSize=\"small\" /> : <CopyIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n </InputAdornment>\n ),\n },\n }}\n />\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AAoBA,MAAM,+BAA+B;AACrC,MAAM,iBAAiB;AAWvB,SAAwB,YAAY,EAAC,OAAO,OAAO,aAAa,KAAK,UAAU,gBAAgB,SAAQ;AACnG,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,UAAU,WAAW;AACrC,QAAM,CAAC,YAAY,kBAAkB,IAAI,SAAS,KAAK;AACvD,QAAM,gBAAgB,CAAC,mBAAmB;AACtC,uBAAmB,cAAc;AACjC,qBAAiB,cAAc;AAAA,EACnC;AACA,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,cAAc,aAAa,EAAE,mBAAmB,IAAI,EAAE,mBAAmB;AAE/E,QAAM,OAAO,YAAY;AACrB,QAAI;AACA,YAAM,UAAU,UAAU,UAAU,KAAK;AACzC,kBAAY,IAAI;AAChB,iBAAA;AACA,iBAAW,MAAM,YAAY,KAAK,GAAG,4BAA4B;AAAA,IACrE,QAAQ;AAEJ,oBAAc,IAAI;AAAA,IACtB;AAAA,EACJ;AAEA,SACI,oBAAC,YAAA,EAAW,SAAkB,OAC1B,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,IAAI;AAAA,MACJ,WAAS;AAAA,MACT,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,aAAa,SAAS;AAAA,MAC5B,WAAW;AAAA,QACP,WAAW,EAAC,UAAU,MAAM,YAAY,OAAO,cAAc,OAAO,OAAO,EAAC,YAAY,eAAA,EAAc;AAAA,QACtG,OAAO;AAAA,UACH,cACI,qBAAC,gBAAA,EAAe,UAAS,OACrB,UAAA;AAAA,YAAA,oBAAC,SAAA,EAAQ,OAAO,aACZ,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,aAAa,SAAS,MAAM,cAAc,CAAC,UAAU,GACrF,UAAA,aAAa,oBAAC,2BAAA,EAA0B,UAAS,QAAA,CAAQ,IAAK,oBAAC,wBAAA,EAAuB,UAAS,QAAA,CAAQ,EAAA,CAC5G,GACJ;AAAA,YACA,oBAAC,SAAA,EAAQ,OAAO,WAAW,EAAE,aAAa,IAAI,EAAE,WAAW,GACvD,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,EAAE,WAAW,GAAG,SAAS,MAAM,OAAO,WAAW,YAAY,WAC7F,qBAAW,oBAAC,WAAA,EAAU,UAAS,QAAA,CAAQ,IAAK,oBAAC,UAAA,EAAS,UAAS,QAAA,CAAQ,GAC5E,EAAA,CACJ;AAAA,UAAA,EAAA,CACJ;AAAA,QAAA;AAAA,MAER;AAAA,IACJ;AAAA,EAAA,GAER;AAER;"}
@@ -0,0 +1,142 @@
1
+ "use client";
2
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import Box from "@mui/material/Box";
5
+ import Button from "@mui/material/Button";
6
+ import GlobalStyles from "@mui/material/GlobalStyles";
7
+ import IconButton from "@mui/material/IconButton";
8
+ import Stack from "@mui/material/Stack";
9
+ import Tooltip from "@mui/material/Tooltip";
10
+ import Typography from "@mui/material/Typography";
11
+ import { useTheme } from "@mui/material/styles";
12
+ import CloseRoundedIcon from "@mui/icons-material/CloseRounded";
13
+ import BarActionButton from "../../appShell/BarActionButton.js";
14
+ import BarActions from "../../appShell/BarActions.js";
15
+ import BarCapsule from "../../appShell/BarCapsule.js";
16
+ import { PHONE_MEDIA_QUERY, FLOATING_BOTTOM_SPACE_CSS_VARIABLE, aboveMobileTabBarOf, GLASS_PANEL_GAP_PIXELS } from "../../appShell/shellSizes.js";
17
+ import { usesMobileTabBar } from "../../appShell/tabBarRule.js";
18
+ import { minimalFloatingStylesOf } from "../../theme/components/minimalComponents.js";
19
+ import { isGlassTheme, glassStylesOf } from "../../theme/glassSurface.js";
20
+ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
21
+ const SELECTION_PHONE_LAYOUT = Object.freeze({ top: "top", split: "split" });
22
+ const FLOATING_GAP_PIXELS = 8;
23
+ const FLOATING_BOTTOM_SPACE_PIXELS = 64;
24
+ const PHONE_ONLY_SX = { display: "none", [PHONE_MEDIA_QUERY]: { display: "flex" } };
25
+ function selectionFloatsOnPhone(theme) {
26
+ return usesMobileTabBar(theme);
27
+ }
28
+ function bottomCapsuleSxOf(theme) {
29
+ return {
30
+ ...isGlassTheme(theme) ? glassStylesOf(theme) : minimalFloatingStylesOf(theme),
31
+ position: "fixed",
32
+ left: "50%",
33
+ transform: "translateX(-50%)",
34
+ zIndex: theme.zIndex.appBar,
35
+ alignItems: "center",
36
+ gap: 0.5,
37
+ width: "max-content",
38
+ maxWidth: `calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px)`,
39
+ borderRadius: "999px",
40
+ p: 0.5,
41
+ ...aboveMobileTabBarOf(FLOATING_GAP_PIXELS)
42
+ };
43
+ }
44
+ function SelectionActions({ countText, actions = [], onClear, phoneLayout = SELECTION_PHONE_LAYOUT.top, texts, sx }) {
45
+ const theme = useTheme();
46
+ const { t } = useToolkitTranslation("common", texts);
47
+ const [runningActionKey, setRunningActionKey] = useState(null);
48
+ const floatsOnPhone = selectionFloatsOnPhone(theme);
49
+ const extraSx = Array.isArray(sx) ? sx : [sx];
50
+ const runAction = async (action) => {
51
+ setRunningActionKey(action.key);
52
+ try {
53
+ await action.onClick();
54
+ } finally {
55
+ setRunningActionKey(null);
56
+ }
57
+ };
58
+ const isDisabledOf = (action) => runningActionKey !== null || Boolean(action.disabled);
59
+ const clearButton = /* @__PURE__ */ jsx(Tooltip, { title: t("selection.clear"), children: /* @__PURE__ */ jsx(IconButton, { "aria-label": t("selection.clear"), onClick: onClear, children: /* @__PURE__ */ jsx(CloseRoundedIcon, {}) }) });
60
+ const row = /* @__PURE__ */ jsxs(
61
+ Stack,
62
+ {
63
+ role: "toolbar",
64
+ "aria-label": t("selection.actionsLabel"),
65
+ direction: "row",
66
+ spacing: 1,
67
+ useFlexGap: true,
68
+ sx: [
69
+ { flexGrow: 1, alignItems: "center", flexWrap: "wrap", minHeight: 32 },
70
+ floatsOnPhone && { [PHONE_MEDIA_QUERY]: { display: "none" } },
71
+ ...extraSx
72
+ ],
73
+ children: [
74
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 600, mr: 1 }, children: countText }),
75
+ actions.map((action) => /* @__PURE__ */ jsx(
76
+ Button,
77
+ {
78
+ size: "small",
79
+ color: action.color ?? "primary",
80
+ startIcon: action.icon,
81
+ disabled: isDisabledOf(action),
82
+ onClick: () => runAction(action),
83
+ children: action.label
84
+ },
85
+ action.key
86
+ )),
87
+ /* @__PURE__ */ jsx(Button, { size: "small", color: "inherit", startIcon: /* @__PURE__ */ jsx(CloseRoundedIcon, {}), onClick: onClear, sx: { ml: "auto" }, children: t("selection.clear") })
88
+ ]
89
+ }
90
+ );
91
+ if (!floatsOnPhone) {
92
+ return row;
93
+ }
94
+ const showsBottomCapsule = phoneLayout === SELECTION_PHONE_LAYOUT.split && actions.length > 0;
95
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
96
+ row,
97
+ /* @__PURE__ */ jsx(BarActions, { bare: true, exclusive: true, order: -2, sx: PHONE_ONLY_SX, inlineSx: PHONE_ONLY_SX, children: /* @__PURE__ */ jsxs(BarCapsule, { "aria-label": t("selection.summaryLabel"), sx: { pl: 2 }, children: [
98
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", role: "status", "aria-live": "polite", sx: { fontWeight: 600, whiteSpace: "nowrap", mr: 0.5 }, children: countText }),
99
+ clearButton
100
+ ] }) }),
101
+ phoneLayout === SELECTION_PHONE_LAYOUT.top && actions.length > 0 && /* @__PURE__ */ jsx(BarActions, { label: t("selection.actionsLabel"), exclusive: true, order: -1, sx: PHONE_ONLY_SX, inlineSx: PHONE_ONLY_SX, children: actions.map((action) => /* @__PURE__ */ jsx(
102
+ BarActionButton,
103
+ {
104
+ label: action.label,
105
+ icon: action.icon,
106
+ color: action.color,
107
+ disabled: isDisabledOf(action),
108
+ onClick: () => runAction(action)
109
+ },
110
+ action.key
111
+ )) }),
112
+ showsBottomCapsule && /* @__PURE__ */ jsxs(Fragment, { children: [
113
+ /* @__PURE__ */ jsx(GlobalStyles, { styles: { ":root": { [FLOATING_BOTTOM_SPACE_CSS_VARIABLE]: `${FLOATING_BOTTOM_SPACE_PIXELS}px` } } }),
114
+ /* @__PURE__ */ jsx(Box, { role: "toolbar", "aria-label": t("selection.actionsLabel"), sx: [bottomCapsuleSxOf, PHONE_ONLY_SX], children: actions.map((action) => action.icon ? /* @__PURE__ */ jsx(Tooltip, { title: action.label, children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
115
+ IconButton,
116
+ {
117
+ "aria-label": action.label,
118
+ color: action.color ?? "default",
119
+ disabled: isDisabledOf(action),
120
+ onClick: () => runAction(action),
121
+ children: action.icon
122
+ }
123
+ ) }) }, action.key) : /* @__PURE__ */ jsx(
124
+ Button,
125
+ {
126
+ size: "small",
127
+ color: action.color ?? "primary",
128
+ disabled: isDisabledOf(action),
129
+ onClick: () => runAction(action),
130
+ children: action.label
131
+ },
132
+ action.key
133
+ )) })
134
+ ] })
135
+ ] });
136
+ }
137
+ export {
138
+ SELECTION_PHONE_LAYOUT,
139
+ SelectionActions as default,
140
+ selectionFloatsOnPhone
141
+ };
142
+ //# sourceMappingURL=SelectionActions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionActions.js","sources":["../../../src/ui/actions/SelectionActions.jsx"],"sourcesContent":["// Las acciones de lo elegido en una lista («5 pedidos elegidos · Cancelar · Borrar · Quitar selección»), en el lugar que le\n// toca según el equipo. Una sola pieza para que cada lista con casillas (la tabla de servidor, una propia) no repita dónde\n// va cada cosa:\n// - escritorio, y el teléfono sin barra de pestañas abajo (el tema de MUI sin vidrio): un renglón donde se pone la pieza,\n// cada acción como botón con su texto;\n// - teléfono con vidrio, o minimalista con cápsulas (usesMobileTabBar), según phoneLayout (SELECTION_PHONE_LAYOUT):\n// top (por omisión): arriba, en la barra flotante, «5 elegidos ✕» y las acciones como íconos en su propia cápsula,\n// como Apple separa los grupos; las que no caben, al «⋯» (BarActions);\n// en los dos, mientras hay algo elegido, el renglón de arriba queda solo para lo elegido (BarActions exclusive): las\n// demás cápsulas de la pantalla y la lupa se esconden, como el modo de edición del iPhone;\n// split: como iOS en modo de edición (Fotos, Mail, Archivos): arriba «5 elegidos ✕» y abajo, encima de las pestañas,\n// una cápsula fija con las acciones, donde llega el pulgar. Mientras se ve, pide su alto al marco\n// (FLOATING_BOTTOM_SPACE_CSS_VARIABLE) para no tapar el final de la página.\n// Lo pidió el autor: en el teléfono, configurable, y por omisión todo arriba.\n//\n// Con vidrio, la cápsula de arriba deja sus botones del color del texto (BarCapsule: la regla de la barra); abajo, una acción\n// destructiva (color: 'error') conserva su rojo, como pide la guía de Apple para lo que borra.\n//\n// Cada acción puede regresar una promesa: mientras corre, las demás se apagan (nadie cancela dos veces lo mismo).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport GlobalStyles from '@mui/material/GlobalStyles';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport BarActionButton from '@/appShell/BarActionButton.jsx';\nimport BarActions from '@/appShell/BarActions.jsx';\nimport BarCapsule from '@/appShell/BarCapsule.jsx';\nimport {\n FLOATING_BOTTOM_SPACE_CSS_VARIABLE, GLASS_PANEL_GAP_PIXELS, PHONE_MEDIA_QUERY, aboveMobileTabBarOf,\n} from '@/appShell/shellSizes.js';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/** Dónde van las acciones de lo elegido en el teléfono con barra de pestañas abajo: todo arriba o divididas (iOS). */\nexport const SELECTION_PHONE_LAYOUT = Object.freeze({top: 'top', split: 'split'});\n\n// lo que se separa la cápsula de abajo de la barra de pestañas (px), lo mismo que el aviso de versión nueva\nconst FLOATING_GAP_PIXELS = 8;\n// lo que pide al marco mientras flota abajo: su alto (48) y su separación, con aire\nconst FLOATING_BOTTOM_SPACE_PIXELS = 64;\n// lo que solo se ve en el teléfono\nconst PHONE_ONLY_SX = {display: 'none', [PHONE_MEDIA_QUERY]: {display: 'flex'}};\n\n/**\n * ¿En el teléfono lo elegido sale de su renglón (arriba o abajo)? La misma regla que la barra de pestañas de abajo: con ella,\n * el renglón de la pantalla se queda con lo suyo (el conteo de la tabla) y las acciones flotan.\n *\n * @param {object} theme\n * @returns {boolean}\n */\nexport function selectionFloatsOnPhone(theme) {\n return usesMobileTabBar(theme);\n}\n\n/**\n * La cápsula fija abajo, centrada en la ventana, encima de la barra de pestañas.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction bottomCapsuleSxOf(theme) {\n return {\n ...(isGlassTheme(theme) ? glassStylesOf(theme) : minimalFloatingStylesOf(theme)),\n position: 'fixed',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: theme.zIndex.appBar,\n alignItems: 'center',\n gap: 0.5,\n width: 'max-content',\n maxWidth: `calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px)`,\n borderRadius: '999px',\n p: 0.5,\n ...aboveMobileTabBarOf(FLOATING_GAP_PIXELS),\n };\n}\n\n/**\n * @param {object} props\n * @param {string} props.countText «5 pedidos elegidos», ya traducido. La pieza se pinta mientras la pantalla la ponga: con\n * nada elegido, no la ponga\n * @param {Array<{key: string, label: string, icon?: import('react').ReactNode, color?: 'inherit'|'primary'|'secondary'|'error'|'info'|'success'|'warning', disabled?: boolean, onClick: () => unknown}>} [props.actions]\n * label ya traducido; en el teléfono se ve el ícono (sin él, el texto). onClick puede regresar una promesa\n * @param {() => void} props.onClear quitar la selección\n * @param {'top'|'split'} [props.phoneLayout] SELECTION_PHONE_LAYOUT; por omisión todo arriba\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {selection: {clear, actionsLabel, summaryLabel}}\n * @param {object} [props.sx] estilos del renglón\n */\nexport default function SelectionActions({countText, actions = [], onClear, phoneLayout = SELECTION_PHONE_LAYOUT.top, texts, sx}) {\n const theme = useTheme();\n const {t} = useToolkitTranslation('common', texts);\n const [runningActionKey, setRunningActionKey] = useState(null);\n const floatsOnPhone = selectionFloatsOnPhone(theme);\n const extraSx = Array.isArray(sx) ? sx : [sx];\n\n const runAction = async (action) => {\n setRunningActionKey(action.key);\n try {\n await action.onClick();\n } finally {\n setRunningActionKey(null);\n }\n };\n const isDisabledOf = (action) => runningActionKey !== null || Boolean(action.disabled);\n\n const clearButton = (\n <Tooltip title={t('selection.clear')}>\n <IconButton aria-label={t('selection.clear')} onClick={onClear}>\n <CloseRoundedIcon />\n </IconButton>\n </Tooltip>\n );\n\n // el renglón: escritorio siempre; en el teléfono, solo si ahí no flota\n const row = (\n <Stack role=\"toolbar\" aria-label={t('selection.actionsLabel')} direction=\"row\" spacing={1} useFlexGap\n sx={[{flexGrow: 1, alignItems: 'center', flexWrap: 'wrap', minHeight: 32},\n floatsOnPhone && {[PHONE_MEDIA_QUERY]: {display: 'none'}}, ...extraSx]}>\n <Typography variant=\"body2\" sx={{fontWeight: 600, mr: 1}}>{countText}</Typography>\n {actions.map((action) => (\n <Button key={action.key} size=\"small\" color={action.color ?? 'primary'} startIcon={action.icon}\n disabled={isDisabledOf(action)} onClick={() => runAction(action)}>\n {action.label}\n </Button>\n ))}\n <Button size=\"small\" color=\"inherit\" startIcon={<CloseRoundedIcon />} onClick={onClear} sx={{ml: 'auto'}}>\n {t('selection.clear')}\n </Button>\n </Stack>\n );\n\n if (!floatsOnPhone) {\n return row;\n }\n\n const showsBottomCapsule = phoneLayout === SELECTION_PHONE_LAYOUT.split && actions.length > 0;\n\n return (\n <>\n {row}\n {/* arriba: con las demás cápsulas flotantes de la pantalla (BarActions), antes que ellas. Sin label en BarActions:\n el nombre ya lo lleva la cápsula (dos grupos anidados con el mismo nombre se leen dos veces) */}\n <BarActions bare exclusive order={-2} sx={PHONE_ONLY_SX} inlineSx={PHONE_ONLY_SX}>\n <BarCapsule aria-label={t('selection.summaryLabel')} sx={{pl: 2}}>\n <Typography variant=\"body2\" role=\"status\" aria-live=\"polite\" sx={{fontWeight: 600, whiteSpace: 'nowrap', mr: 0.5}}>\n {countText}\n </Typography>\n {clearButton}\n </BarCapsule>\n </BarActions>\n {phoneLayout === SELECTION_PHONE_LAYOUT.top && actions.length > 0 && (\n <BarActions label={t('selection.actionsLabel')} exclusive order={-1} sx={PHONE_ONLY_SX} inlineSx={PHONE_ONLY_SX}>\n {actions.map((action) => (\n <BarActionButton key={action.key} label={action.label} icon={action.icon} color={action.color}\n disabled={isDisabledOf(action)} onClick={() => runAction(action)} />\n ))}\n </BarActions>\n )}\n {showsBottomCapsule && (\n <>\n <GlobalStyles styles={{':root': {[FLOATING_BOTTOM_SPACE_CSS_VARIABLE]: `${FLOATING_BOTTOM_SPACE_PIXELS}px`}}} />\n <Box role=\"toolbar\" aria-label={t('selection.actionsLabel')} sx={[bottomCapsuleSxOf, PHONE_ONLY_SX]}>\n {actions.map((action) => (action.icon ? (\n <Tooltip key={action.key} title={action.label}>\n {/* el span deja ver el tooltip aunque el botón esté apagado */}\n <span>\n <IconButton aria-label={action.label} color={action.color ?? 'default'} disabled={isDisabledOf(action)}\n onClick={() => runAction(action)}>\n {action.icon}\n </IconButton>\n </span>\n </Tooltip>\n ) : (\n <Button key={action.key} size=\"small\" color={action.color ?? 'primary'} disabled={isDisabledOf(action)}\n onClick={() => runAction(action)}>\n {action.label}\n </Button>\n )))}\n </Box>\n </>\n )}\n </>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -9,9 +9,10 @@ import Stack from "@mui/material/Stack";
9
9
  import TextField from "@mui/material/TextField";
10
10
  import Typography from "@mui/material/Typography";
11
11
  import DateRangeOutlinedIcon from "@mui/icons-material/DateRangeOutlined";
12
- import { formatDate } from "../../formatting.js";
13
- function DateRangeFilter({ from = "", to = "", onChange, label }) {
14
- const { t } = useToolkitTranslation("table");
12
+ import { useFormatters } from "../../toolkit/useFormatters.js";
13
+ function DateRangeFilter({ from = "", to = "", onChange, label, texts }) {
14
+ const { t } = useToolkitTranslation("table", texts);
15
+ const { formatDate } = useFormatters();
15
16
  const [anchorElement, setAnchorElement] = useState(null);
16
17
  const [draft, setDraft] = useState({ from, to });
17
18
  const isActive = Boolean(from || to);
@@ -1 +1 @@
1
- {"version":3,"file":"DateRangeFilter.js","sources":["../../../src/ui/filters/DateRangeFilter.jsx"],"sourcesContent":["// Un rango de fechas personalizado, como chip al lado de los accesos rápidos.\n//\n// Viaja como AAAA-MM-DD (día, no instante). Convertirlo a \"inicio del día\" y \"fin del día\" lo hace el SERVIDOR con la\n// zona del negocio: si lo hiciera el navegador con la suya, alguien en otra zona pediría otro rango sin saberlo.\n//\n// El calendario es el <input type=\"date\"> del TextField de MUI y no el DatePicker de @mui/x-date-pickers con moment,\n// para no sumar dos dependencias (x-date-pickers y moment) que ninguna\n// pantalla del 22-sep necesita. Si el calendario nativo estorba (Safari de escritorio), se cambia SOLO este archivo.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Popover from '@mui/material/Popover';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\nimport DateRangeOutlinedIcon from '@mui/icons-material/DateRangeOutlined';\n\nimport {formatDate} from '@/formatting.js';\n\n/**\n * @param {object} props\n * @param {string} [props.from] AAAA-MM-DD o '' si no hay\n * @param {string} [props.to] AAAA-MM-DD o '' si no hay\n * @param {(range: {from: string, to: string}) => void} props.onChange '' para quitar\n * @param {string} [props.label] ya traducido; por defecto \"Personalizado\"\n */\nexport default function DateRangeFilter({from = '', to = '', onChange, label}) {\n const {t} = useToolkitTranslation('table');\n const [anchorElement, setAnchorElement] = useState(null);\n const [draft, setDraft] = useState({from, to});\n const isActive = Boolean(from || to);\n // AAAA-MM-DD se compara bien como texto\n const isInvalid = Boolean(draft.from && draft.to && draft.from > draft.to);\n\n const open = (event) => {\n setDraft({from, to});\n setAnchorElement(event.currentTarget);\n };\n const close = () => setAnchorElement(null);\n const apply = () => {\n onChange(draft);\n close();\n };\n const clear = () => {\n onChange({from: '', to: ''});\n close();\n };\n\n const summary = isActive\n ? t('dateRange.summary', {from: from ? formatDate(from) : '…', to: to ? formatDate(to) : t('dateRange.today')})\n : (label ?? t('dateRange.label'));\n\n return (\n <>\n <Chip icon={<DateRangeOutlinedIcon />} label={summary} clickable onClick={open}\n variant={isActive ? 'filled' : 'outlined'} color={isActive ? 'primary' : 'default'}\n onDelete={isActive ? clear : undefined} sx={{flex: '0 0 auto', height: 32}} />\n <Popover open={Boolean(anchorElement)} anchorEl={anchorElement} onClose={close} anchorOrigin={{vertical: 'bottom', horizontal: 'left'}}>\n <Stack spacing={2} sx={{p: 2, width: 280}}>\n <Typography variant=\"subtitle2\">{t('dateRange.title')}</Typography>\n <TextField type=\"date\" size=\"small\" fullWidth label={t('dateRange.from')} value={draft.from}\n helperText={t('dateRange.fromHelp')}\n onChange={(event) => setDraft((currentDraft) => ({...currentDraft, from: event.target.value}))}\n slotProps={{inputLabel: {shrink: true}, htmlInput: {max: draft.to || undefined}}} />\n <TextField type=\"date\" size=\"small\" fullWidth label={t('dateRange.to')} value={draft.to} error={isInvalid}\n helperText={isInvalid ? t('dateRange.invalid') : t('dateRange.toHelp')}\n onChange={(event) => setDraft((currentDraft) => ({...currentDraft, to: event.target.value}))}\n slotProps={{inputLabel: {shrink: true}, htmlInput: {min: draft.from || undefined}}} />\n <Stack direction=\"row\" spacing={1} sx={{justifyContent: 'flex-end'}}>\n {isActive && <Button onClick={clear}>{t('dateRange.clear')}</Button>}\n <Button variant=\"contained\" onClick={apply} disabled={isInvalid || !(draft.from || draft.to)}>\n {t('dateRange.apply')}\n </Button>\n </Stack>\n </Stack>\n </Popover>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA6BA,SAAwB,gBAAgB,EAAC,OAAO,IAAI,KAAK,IAAI,UAAU,SAAQ;AAC3E,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AACzC,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AACvD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAC,MAAM,IAAG;AAC7C,QAAM,WAAW,QAAQ,QAAQ,EAAE;AAEnC,QAAM,YAAY,QAAQ,MAAM,QAAQ,MAAM,MAAM,MAAM,OAAO,MAAM,EAAE;AAEzE,QAAM,OAAO,CAAC,UAAU;AACpB,aAAS,EAAC,MAAM,IAAG;AACnB,qBAAiB,MAAM,aAAa;AAAA,EACxC;AACA,QAAM,QAAQ,MAAM,iBAAiB,IAAI;AACzC,QAAM,QAAQ,MAAM;AAChB,aAAS,KAAK;AACd,UAAA;AAAA,EACJ;AACA,QAAM,QAAQ,MAAM;AAChB,aAAS,EAAC,MAAM,IAAI,IAAI,IAAG;AAC3B,UAAA;AAAA,EACJ;AAEA,QAAM,UAAU,WACV,EAAE,qBAAqB,EAAC,MAAM,OAAO,WAAW,IAAI,IAAI,KAAK,IAAI,KAAK,WAAW,EAAE,IAAI,EAAE,iBAAiB,GAAE,IAC3G,SAAS,EAAE,iBAAiB;AAEnC,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAK,0BAAO,uBAAA,EAAsB;AAAA,QAAI,OAAO;AAAA,QAAS,WAAS;AAAA,QAAC,SAAS;AAAA,QACpE,SAAS,WAAW,WAAW;AAAA,QAAY,OAAO,WAAW,YAAY;AAAA,QACzE,UAAU,WAAW,QAAQ;AAAA,QAAW,IAAI,EAAC,MAAM,YAAY,QAAQ,GAAA;AAAA,MAAE;AAAA,IAAA;AAAA,IAC/E,oBAAC,SAAA,EAAQ,MAAM,QAAQ,aAAa,GAAG,UAAU,eAAe,SAAS,OAAO,cAAc,EAAC,UAAU,UAAU,YAAY,OAAA,GAC3H,UAAA,qBAAC,OAAA,EAAM,SAAS,GAAG,IAAI,EAAC,GAAG,GAAG,OAAO,IAAA,GACjC,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,aAAa,UAAA,EAAE,iBAAiB,GAAE;AAAA,MACtD;AAAA,QAAC;AAAA,QAAA;AAAA,UAAU,MAAK;AAAA,UAAO,MAAK;AAAA,UAAQ,WAAS;AAAA,UAAC,OAAO,EAAE,gBAAgB;AAAA,UAAG,OAAO,MAAM;AAAA,UAC5E,YAAY,EAAE,oBAAoB;AAAA,UAClC,UAAU,CAAC,UAAU,SAAS,CAAC,kBAAkB,EAAC,GAAG,cAAc,MAAM,MAAM,OAAO,QAAO;AAAA,UAC7F,WAAW,EAAC,YAAY,EAAC,QAAQ,KAAA,GAAO,WAAW,EAAC,KAAK,MAAM,MAAM,SAAS;AAAA,QAAC;AAAA,MAAA;AAAA,MAC1F;AAAA,QAAC;AAAA,QAAA;AAAA,UAAU,MAAK;AAAA,UAAO,MAAK;AAAA,UAAQ,WAAS;AAAA,UAAC,OAAO,EAAE,cAAc;AAAA,UAAG,OAAO,MAAM;AAAA,UAAI,OAAO;AAAA,UACrF,YAAY,YAAY,EAAE,mBAAmB,IAAI,EAAE,kBAAkB;AAAA,UACrE,UAAU,CAAC,UAAU,SAAS,CAAC,kBAAkB,EAAC,GAAG,cAAc,IAAI,MAAM,OAAO,QAAO;AAAA,UAC3F,WAAW,EAAC,YAAY,EAAC,QAAQ,KAAA,GAAO,WAAW,EAAC,KAAK,MAAM,QAAQ,SAAS;AAAA,QAAC;AAAA,MAAA;AAAA,MAC5F,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,gBAAgB,WAAA,GACnD,UAAA;AAAA,QAAA,gCAAa,QAAA,EAAO,SAAS,OAAQ,UAAA,EAAE,iBAAiB,GAAE;AAAA,QAC3D,oBAAC,QAAA,EAAO,SAAQ,aAAY,SAAS,OAAO,UAAU,aAAa,EAAE,MAAM,QAAQ,MAAM,KACpF,UAAA,EAAE,iBAAiB,EAAA,CACxB;AAAA,MAAA,EAAA,CACJ;AAAA,IAAA,EAAA,CACJ,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"DateRangeFilter.js","sources":["../../../src/ui/filters/DateRangeFilter.jsx"],"sourcesContent":["// Un rango de fechas personalizado, como chip al lado de los accesos rápidos.\n//\n// Viaja como AAAA-MM-DD (día, no instante). Convertirlo a \"inicio del día\" y \"fin del día\" lo hace el SERVIDOR con la\n// zona del negocio: si lo hiciera el navegador con la suya, alguien en otra zona pediría otro rango sin saberlo.\n//\n// El calendario es el <input type=\"date\"> del TextField de MUI y no el DatePicker de @mui/x-date-pickers con moment,\n// para no sumar dos dependencias (x-date-pickers y moment) que ninguna\n// pantalla del 22-sep necesita. Si el calendario nativo estorba (Safari de escritorio), se cambia SOLO este archivo.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Popover from '@mui/material/Popover';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\nimport DateRangeOutlinedIcon from '@mui/icons-material/DateRangeOutlined';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\n\n/**\n * @param {object} props\n * @param {string} [props.from] AAAA-MM-DD o '' si no hay\n * @param {string} [props.to] AAAA-MM-DD o '' si no hay\n * @param {(range: {from: string, to: string}) => void} props.onChange '' para quitar\n * @param {string} [props.label] ya traducido; por defecto \"Personalizado\"\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function DateRangeFilter({from = '', to = '', onChange, label, texts}) {\n const {t} = useToolkitTranslation('table', texts);\n const {formatDate} = useFormatters();\n const [anchorElement, setAnchorElement] = useState(null);\n const [draft, setDraft] = useState({from, to});\n const isActive = Boolean(from || to);\n // AAAA-MM-DD se compara bien como texto\n const isInvalid = Boolean(draft.from && draft.to && draft.from > draft.to);\n\n const open = (event) => {\n setDraft({from, to});\n setAnchorElement(event.currentTarget);\n };\n const close = () => setAnchorElement(null);\n const apply = () => {\n onChange(draft);\n close();\n };\n const clear = () => {\n onChange({from: '', to: ''});\n close();\n };\n\n const summary = isActive\n ? t('dateRange.summary', {from: from ? formatDate(from) : '…', to: to ? formatDate(to) : t('dateRange.today')})\n : (label ?? t('dateRange.label'));\n\n return (\n <>\n <Chip icon={<DateRangeOutlinedIcon />} label={summary} clickable onClick={open}\n variant={isActive ? 'filled' : 'outlined'} color={isActive ? 'primary' : 'default'}\n onDelete={isActive ? clear : undefined} sx={{flex: '0 0 auto', height: 32}} />\n <Popover open={Boolean(anchorElement)} anchorEl={anchorElement} onClose={close} anchorOrigin={{vertical: 'bottom', horizontal: 'left'}}>\n <Stack spacing={2} sx={{p: 2, width: 280}}>\n <Typography variant=\"subtitle2\">{t('dateRange.title')}</Typography>\n <TextField type=\"date\" size=\"small\" fullWidth label={t('dateRange.from')} value={draft.from}\n helperText={t('dateRange.fromHelp')}\n onChange={(event) => setDraft((currentDraft) => ({...currentDraft, from: event.target.value}))}\n slotProps={{inputLabel: {shrink: true}, htmlInput: {max: draft.to || undefined}}} />\n <TextField type=\"date\" size=\"small\" fullWidth label={t('dateRange.to')} value={draft.to} error={isInvalid}\n helperText={isInvalid ? t('dateRange.invalid') : t('dateRange.toHelp')}\n onChange={(event) => setDraft((currentDraft) => ({...currentDraft, to: event.target.value}))}\n slotProps={{inputLabel: {shrink: true}, htmlInput: {min: draft.from || undefined}}} />\n <Stack direction=\"row\" spacing={1} sx={{justifyContent: 'flex-end'}}>\n {isActive && <Button onClick={clear}>{t('dateRange.clear')}</Button>}\n <Button variant=\"contained\" onClick={apply} disabled={isInvalid || !(draft.from || draft.to)}>\n {t('dateRange.apply')}\n </Button>\n </Stack>\n </Stack>\n </Popover>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -24,8 +24,8 @@ function compactCount(count) {
24
24
  }
25
25
  return String(count);
26
26
  }
27
- function QuickFilters({ value, onChange, options = [], caption, ariaLabel, extra }) {
28
- const { t } = useToolkitTranslation("table");
27
+ function QuickFilters({ value, onChange, options = [], caption, ariaLabel, extra, texts }) {
28
+ const { t } = useToolkitTranslation("table", texts);
29
29
  const activeChip = useRef(null);
30
30
  const isFirstRender = useRef(true);
31
31
  useEffect(() => {