@stackcraftmx/calcita 0.23.0 → 0.26.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 (403) hide show
  1. package/README.md +127 -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 +40 -5
  44. package/dist/api/createApiClient.js.map +1 -1
  45. package/dist/appShell/AccountMenu.js +15 -23
  46. package/dist/appShell/AccountMenu.js.map +1 -1
  47. package/dist/appShell/AppLayout.js +42 -15
  48. package/dist/appShell/AppLayout.js.map +1 -1
  49. package/dist/appShell/AppearanceSettings.js +29 -6
  50. package/dist/appShell/AppearanceSettings.js.map +1 -1
  51. package/dist/appShell/AuthLayout.js +2 -0
  52. package/dist/appShell/AuthLayout.js.map +1 -1
  53. package/dist/appShell/BarActionButton.js +3 -3
  54. package/dist/appShell/BarActionButton.js.map +1 -1
  55. package/dist/appShell/BarActions.js +19 -8
  56. package/dist/appShell/BarActions.js.map +1 -1
  57. package/dist/appShell/BarCapsule.js +19 -4
  58. package/dist/appShell/BarCapsule.js.map +1 -1
  59. package/dist/appShell/BarSearch.js +92 -0
  60. package/dist/appShell/BarSearch.js.map +1 -0
  61. package/dist/appShell/HeaderTools.js +4 -2
  62. package/dist/appShell/HeaderTools.js.map +1 -1
  63. package/dist/appShell/MenuLinks.js +36 -0
  64. package/dist/appShell/MenuLinks.js.map +1 -0
  65. package/dist/appShell/MobileTabBar.js +6 -4
  66. package/dist/appShell/MobileTabBar.js.map +1 -1
  67. package/dist/appShell/SettingsMenu.js +10 -5
  68. package/dist/appShell/SettingsMenu.js.map +1 -1
  69. package/dist/appShell/SideMenu.js +5 -3
  70. package/dist/appShell/SideMenu.js.map +1 -1
  71. package/dist/appShell/SideMenuEntry.js +3 -3
  72. package/dist/appShell/SideMenuEntry.js.map +1 -1
  73. package/dist/appShell/SubmenuActionsMenu.js +3 -4
  74. package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
  75. package/dist/appShell/TopMenu.js +5 -6
  76. package/dist/appShell/TopMenu.js.map +1 -1
  77. package/dist/appShell/Wordmark.js +11 -0
  78. package/dist/appShell/Wordmark.js.map +1 -0
  79. package/dist/appShell/menuItemText.js +4 -0
  80. package/dist/appShell/menuItemText.js.map +1 -1
  81. package/dist/appShell/shellSizes.js +2 -0
  82. package/dist/appShell/shellSizes.js.map +1 -1
  83. package/dist/auth/PasswordChecklist.js +2 -2
  84. package/dist/auth/PasswordChecklist.js.map +1 -1
  85. package/dist/calendar/EventCalendar.js +16 -5
  86. package/dist/calendar/EventCalendar.js.map +1 -1
  87. package/dist/calendar/EventDetails.js +74 -0
  88. package/dist/calendar/EventDetails.js.map +1 -0
  89. package/dist/calendar/calendarEvents.js +31 -0
  90. package/dist/calendar/calendarEvents.js.map +1 -0
  91. package/dist/calendar.js +8 -1
  92. package/dist/calendar.js.map +1 -1
  93. package/dist/charts.js +10 -1
  94. package/dist/charts.js.map +1 -1
  95. package/dist/datePickers/DateInput.js +3 -2
  96. package/dist/datePickers/DateInput.js.map +1 -1
  97. package/dist/downloads/DownloadManager.js +14 -20
  98. package/dist/downloads/DownloadManager.js.map +1 -1
  99. package/dist/downloads/downloadQueue.js +4 -2
  100. package/dist/downloads/downloadQueue.js.map +1 -1
  101. package/dist/downloads/downloadsMachine.js +4 -1
  102. package/dist/downloads/downloadsMachine.js.map +1 -1
  103. package/dist/downloads/fileTypes.js +22 -0
  104. package/dist/downloads/fileTypes.js.map +1 -0
  105. package/dist/feedback/ConfirmDialog.js +3 -2
  106. package/dist/feedback/ConfirmDialog.js.map +1 -1
  107. package/dist/feedback/ModalWaiting.js +2 -2
  108. package/dist/feedback/ModalWaiting.js.map +1 -1
  109. package/dist/feedback/NewBuildNotice.js +2 -2
  110. package/dist/feedback/NewBuildNotice.js.map +1 -1
  111. package/dist/filepond/FileDropzone.js +5 -3
  112. package/dist/filepond/FileDropzone.js.map +1 -1
  113. package/dist/formatting.js +171 -143
  114. package/dist/formatting.js.map +1 -1
  115. package/dist/forms/FormWizard.js +18 -45
  116. package/dist/forms/FormWizard.js.map +1 -1
  117. package/dist/forms/auth/ChangePasswordForm.js +5 -4
  118. package/dist/forms/auth/ChangePasswordForm.js.map +1 -1
  119. package/dist/forms/auth/ForgotPasswordForm.js +2 -2
  120. package/dist/forms/auth/ForgotPasswordForm.js.map +1 -1
  121. package/dist/forms/auth/SignInForm.js +2 -2
  122. package/dist/forms/auth/SignInForm.js.map +1 -1
  123. package/dist/forms/security/SecondStepForm.js +2 -2
  124. package/dist/forms/security/SecondStepForm.js.map +1 -1
  125. package/dist/forms/security/TwoStepVerificationCard.js +8 -6
  126. package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
  127. package/dist/hookForm/FormWizard.js +59 -0
  128. package/dist/hookForm/FormWizard.js.map +1 -0
  129. package/dist/inbox/InboxLayout.js +44 -8
  130. package/dist/inbox/InboxLayout.js.map +1 -1
  131. package/dist/inbox/MessageList.js +71 -21
  132. package/dist/inbox/MessageList.js.map +1 -1
  133. package/dist/inbox/MessageView.js +80 -29
  134. package/dist/inbox/MessageView.js.map +1 -1
  135. package/dist/inbox/inboxDates.js +4 -3
  136. package/dist/inbox/inboxDates.js.map +1 -1
  137. package/dist/index.js +163 -136
  138. package/dist/index.js.map +1 -1
  139. package/dist/inputs/CatalogInput.js +8 -4
  140. package/dist/inputs/CatalogInput.js.map +1 -1
  141. package/dist/inputs/ColorInput.js +3 -2
  142. package/dist/inputs/ColorInput.js.map +1 -1
  143. package/dist/inputs/FormSection.js +3 -3
  144. package/dist/inputs/FormSection.js.map +1 -1
  145. package/dist/inputs/NumberInput.js +3 -2
  146. package/dist/inputs/NumberInput.js.map +1 -1
  147. package/dist/inputs/SelectInput.js +29 -11
  148. package/dist/inputs/SelectInput.js.map +1 -1
  149. package/dist/inputs/TextInput.js +6 -2
  150. package/dist/inputs/TextInput.js.map +1 -1
  151. package/dist/inputs/WizardLayout.js +54 -0
  152. package/dist/inputs/WizardLayout.js.map +1 -0
  153. package/dist/kanban/KanbanBoard.js +239 -14
  154. package/dist/kanban/KanbanBoard.js.map +1 -1
  155. package/dist/kanban/kanbanCards.js +19 -0
  156. package/dist/kanban/kanbanCards.js.map +1 -0
  157. package/dist/kanban.js +7 -1
  158. package/dist/kanban.js.map +1 -1
  159. package/dist/locales/en.js +74 -1
  160. package/dist/locales/en.js.map +1 -1
  161. package/dist/locales/es.js +74 -1
  162. package/dist/locales/es.js.map +1 -1
  163. package/dist/maps/MapView.js +28 -8
  164. package/dist/maps/MapView.js.map +1 -1
  165. package/dist/navigation/appMenu.js +13 -1
  166. package/dist/navigation/appMenu.js.map +1 -1
  167. package/dist/observability/logger.js +18 -0
  168. package/dist/observability/logger.js.map +1 -1
  169. package/dist/observability/usePublishedBuildWatch.js +4 -3
  170. package/dist/observability/usePublishedBuildWatch.js.map +1 -1
  171. package/dist/pwa/InstallAppSetting.js +6 -5
  172. package/dist/pwa/InstallAppSetting.js.map +1 -1
  173. package/dist/pwa/InstallInstructionsDialog.js +2 -2
  174. package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
  175. package/dist/pwa/InstallPromptBanner.js +15 -9
  176. package/dist/pwa/InstallPromptBanner.js.map +1 -1
  177. package/dist/pwa/useInstallPrompt.js +2 -1
  178. package/dist/pwa/useInstallPrompt.js.map +1 -1
  179. package/dist/react-hook-form.js +6 -4
  180. package/dist/react-hook-form.js.map +1 -1
  181. package/dist/security/ActivityLog.js +4 -3
  182. package/dist/security/ActivityLog.js.map +1 -1
  183. package/dist/security/RecoveryCodesDialog.js +4 -3
  184. package/dist/security/RecoveryCodesDialog.js.map +1 -1
  185. package/dist/security/SessionsCard.js +8 -6
  186. package/dist/security/SessionsCard.js.map +1 -1
  187. package/dist/table.js +2 -0
  188. package/dist/table.js.map +1 -1
  189. package/dist/theme/components/MuiAppBar.js +6 -5
  190. package/dist/theme/components/MuiAppBar.js.map +1 -1
  191. package/dist/theme/components/MuiDialog.js +13 -0
  192. package/dist/theme/components/MuiDialog.js.map +1 -0
  193. package/dist/theme/components/glassComponents.js +32 -31
  194. package/dist/theme/components/glassComponents.js.map +1 -1
  195. package/dist/theme/components/minimalComponents.js +8 -4
  196. package/dist/theme/components/minimalComponents.js.map +1 -1
  197. package/dist/theme/createToolkitTheme.js.map +1 -1
  198. package/dist/theme/glassSurface.js +36 -38
  199. package/dist/theme/glassSurface.js.map +1 -1
  200. package/dist/theme/lightDarkStyles.js +48 -0
  201. package/dist/theme/lightDarkStyles.js.map +1 -0
  202. package/dist/theme/toolkitComponents.js +2 -0
  203. package/dist/theme/toolkitComponents.js.map +1 -1
  204. package/dist/toolkit/ComponentTexts.js +13 -0
  205. package/dist/toolkit/ComponentTexts.js.map +1 -0
  206. package/dist/toolkit/ToolkitProvider.js +36 -28
  207. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  208. package/dist/toolkit/browserNavigation.js +19 -20
  209. package/dist/toolkit/browserNavigation.js.map +1 -1
  210. package/dist/toolkit/componentTextsContext.js +15 -0
  211. package/dist/toolkit/componentTextsContext.js.map +1 -0
  212. package/dist/toolkit/hashNavigation.js +40 -0
  213. package/dist/toolkit/hashNavigation.js.map +1 -0
  214. package/dist/toolkit/navigationPaths.js +49 -0
  215. package/dist/toolkit/navigationPaths.js.map +1 -0
  216. package/dist/toolkit/toolkitSettings.js +24 -4
  217. package/dist/toolkit/toolkitSettings.js.map +1 -1
  218. package/dist/toolkit/useFormatters.js +12 -0
  219. package/dist/toolkit/useFormatters.js.map +1 -0
  220. package/dist/toolkit/useToolkitTranslation.js +11 -7
  221. package/dist/toolkit/useToolkitTranslation.js.map +1 -1
  222. package/dist/types/DataTable/AuditTrailTable.d.ts +3 -1
  223. package/dist/types/DataTable/EmptyState.d.ts +7 -2
  224. package/dist/types/DataTable/FilterChips.d.ts +3 -1
  225. package/dist/types/DataTable/GoToPage.d.ts +7 -2
  226. package/dist/types/DataTable/SelectionBar.d.ts +32 -0
  227. package/dist/types/DataTable/ServerTable.d.ts +64 -3
  228. package/dist/types/DataTable/index.d.ts +1 -1
  229. package/dist/types/DataTable/rowSelection.d.ts +113 -0
  230. package/dist/types/access/PermissionMatrix.d.ts +3 -1
  231. package/dist/types/account/AccountSecuritySection.d.ts +3 -1
  232. package/dist/types/account/ForgotPasswordScreen.d.ts +3 -1
  233. package/dist/types/account/ProfileScreen.d.ts +31 -1
  234. package/dist/types/account/ResetPasswordScreen.d.ts +3 -1
  235. package/dist/types/account/SignInScreen.d.ts +3 -1
  236. package/dist/types/account/accountConnector.d.ts +7 -2
  237. package/dist/types/adapters/reactRouterRoutes.d.ts +14 -2
  238. package/dist/types/api/createApiClient.d.ts +46 -1
  239. package/dist/types/appShell/AppLayout.d.ts +15 -1
  240. package/dist/types/appShell/AppearanceSettings.d.ts +8 -1
  241. package/dist/types/appShell/BarActionButton.d.ts +2 -2
  242. package/dist/types/appShell/BarActions.d.ts +6 -1
  243. package/dist/types/appShell/BarCapsule.d.ts +8 -0
  244. package/dist/types/appShell/BarSearch.d.ts +24 -0
  245. package/dist/types/appShell/MenuLinks.d.ts +17 -0
  246. package/dist/types/appShell/MobileTabBar.d.ts +4 -1
  247. package/dist/types/appShell/SettingsMenu.d.ts +3 -1
  248. package/dist/types/appShell/SideMenu.d.ts +3 -1
  249. package/dist/types/appShell/Wordmark.d.ts +6 -0
  250. package/dist/types/appShell/menuItemText.d.ts +16 -0
  251. package/dist/types/appShell/shellSizes.d.ts +1 -0
  252. package/dist/types/auth/PasswordChecklist.d.ts +3 -1
  253. package/dist/types/calendar/EventCalendar.d.ts +18 -1
  254. package/dist/types/calendar/EventDetails.d.ts +36 -0
  255. package/dist/types/calendar/calendarEvents.d.ts +27 -0
  256. package/dist/types/calendar.d.ts +2 -0
  257. package/dist/types/charts.d.ts +4 -0
  258. package/dist/types/datePickers/DateInput.d.ts +3 -1
  259. package/dist/types/downloads/DownloadManager.d.ts +7 -1
  260. package/dist/types/downloads/downloadQueue.d.ts +8 -3
  261. package/dist/types/downloads/fileTypes.d.ts +39 -0
  262. package/dist/types/downloads/index.d.ts +1 -0
  263. package/dist/types/feedback/ConfirmDialog.d.ts +3 -1
  264. package/dist/types/feedback/ModalWaiting.d.ts +7 -2
  265. package/dist/types/feedback/NewBuildNotice.d.ts +3 -1
  266. package/dist/types/filepond/FileDropzone.d.ts +3 -1
  267. package/dist/types/formatting.d.ts +45 -90
  268. package/dist/types/forms/FormWizard.d.ts +3 -1
  269. package/dist/types/forms/auth/ChangePasswordForm.d.ts +3 -1
  270. package/dist/types/forms/auth/ForgotPasswordForm.d.ts +3 -1
  271. package/dist/types/forms/auth/SignInForm.d.ts +3 -1
  272. package/dist/types/forms/security/SecondStepForm.d.ts +3 -1
  273. package/dist/types/forms/security/TwoStepVerificationCard.d.ts +3 -1
  274. package/dist/types/hookForm/FormWizard.d.ts +22 -0
  275. package/dist/types/inbox/InboxLayout.d.ts +14 -2
  276. package/dist/types/inbox/MessageList.d.ts +30 -5
  277. package/dist/types/inbox/MessageView.d.ts +35 -3
  278. package/dist/types/inbox/inboxDates.d.ts +6 -1
  279. package/dist/types/index.d.ts +15 -8
  280. package/dist/types/inputs/CatalogInput.d.ts +9 -5
  281. package/dist/types/inputs/ColorInput.d.ts +3 -1
  282. package/dist/types/inputs/FormSection.d.ts +2 -2
  283. package/dist/types/inputs/NumberInput.d.ts +3 -1
  284. package/dist/types/inputs/SelectInput.d.ts +9 -5
  285. package/dist/types/inputs/TextInput.d.ts +3 -1
  286. package/dist/types/inputs/WizardLayout.d.ts +34 -0
  287. package/dist/types/kanban/KanbanBoard.d.ts +61 -6
  288. package/dist/types/kanban/kanbanCards.d.ts +22 -0
  289. package/dist/types/kanban.d.ts +1 -0
  290. package/dist/types/locales/en.d.ts +77 -0
  291. package/dist/types/locales/es.d.ts +77 -0
  292. package/dist/types/maps/MapView.d.ts +12 -3
  293. package/dist/types/navigation/appMenu.d.ts +8 -0
  294. package/dist/types/observability/logger.d.ts +13 -0
  295. package/dist/types/observability/usePublishedBuildWatch.d.ts +3 -1
  296. package/dist/types/pwa/InstallAppSetting.d.ts +7 -1
  297. package/dist/types/pwa/InstallInstructionsDialog.d.ts +3 -1
  298. package/dist/types/pwa/InstallPromptBanner.d.ts +10 -1
  299. package/dist/types/pwa/useInstallPrompt.d.ts +3 -2
  300. package/dist/types/react-hook-form.d.ts +1 -0
  301. package/dist/types/security/ActivityLog.d.ts +3 -1
  302. package/dist/types/security/RecoveryCodesDialog.d.ts +6 -1
  303. package/dist/types/security/SessionsCard.d.ts +3 -1
  304. package/dist/types/table.d.ts +3 -1
  305. package/dist/types/theme/components/MuiAppBar.d.ts +3 -1
  306. package/dist/types/theme/components/MuiDialog.d.ts +9 -0
  307. package/dist/types/theme/components/glassComponents.d.ts +5 -0
  308. package/dist/types/theme/lightDarkStyles.d.ts +8 -0
  309. package/dist/types/toolkit/ComponentTexts.d.ts +11 -0
  310. package/dist/types/toolkit/ToolkitProvider.d.ts +18 -2
  311. package/dist/types/toolkit/browserNavigation.d.ts +23 -28
  312. package/dist/types/toolkit/componentTextsContext.d.ts +20 -0
  313. package/dist/types/toolkit/hashNavigation.d.ts +12 -0
  314. package/dist/types/toolkit/navigationPaths.d.ts +43 -0
  315. package/dist/types/toolkit/toolkitSettings.d.ts +49 -4
  316. package/dist/types/toolkit/useFormatters.d.ts +6 -0
  317. package/dist/types/toolkit/useToolkitTranslation.d.ts +2 -1
  318. package/dist/types/ui/actions/ActionsMenu.d.ts +20 -0
  319. package/dist/types/ui/actions/CopyButton.d.ts +3 -1
  320. package/dist/types/ui/actions/DownloadMenu.d.ts +8 -3
  321. package/dist/types/ui/actions/SecretField.d.ts +5 -1
  322. package/dist/types/ui/actions/SelectionActions.d.ts +39 -0
  323. package/dist/types/ui/filters/DateRangeFilter.d.ts +3 -1
  324. package/dist/types/ui/filters/QuickFilters.d.ts +3 -1
  325. package/dist/types/ui/index.d.ts +1 -1
  326. package/dist/types/ui/labels/tagOf.d.ts +13 -0
  327. package/dist/types/ui/layout/CollapsibleSection.d.ts +4 -1
  328. package/dist/types/ui/layout/ContentDialog.d.ts +3 -1
  329. package/dist/types/ui/layout/ErrorPage.d.ts +14 -1
  330. package/dist/types/ui/layout/FullPageLoader.d.ts +7 -1
  331. package/dist/types/ui/layout/PageHeading.d.ts +3 -1
  332. package/dist/types/ui/metrics/BarList.d.ts +7 -1
  333. package/dist/types/ui/metrics/CalendarHeatmap.d.ts +7 -1
  334. package/dist/types/ui/metrics/ChartTooltip.d.ts +9 -3
  335. package/dist/types/ui/metrics/ColumnChart.d.ts +23 -11
  336. package/dist/types/ui/metrics/LineChart.d.ts +41 -0
  337. package/dist/types/ui/metrics/MetricCard.d.ts +9 -1
  338. package/dist/types/ui/metrics/TableToggle.d.ts +9 -2
  339. package/dist/types/ui/metrics/chartParts.d.ts +112 -0
  340. package/dist/types/ui/navigation/BackButton.d.ts +3 -1
  341. package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +8 -2
  342. package/dist/types/ui/navigation/FontSizeToggle.d.ts +7 -2
  343. package/dist/types/ui/navigation/LanguageToggle.d.ts +8 -3
  344. package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +5 -2
  345. package/dist/types/ui/timeline/GroupedTimeline.d.ts +4 -1
  346. package/dist/types/upload/FileDropzone.d.ts +3 -1
  347. package/dist/ui/actions/ActionsMenu.js +51 -0
  348. package/dist/ui/actions/ActionsMenu.js.map +1 -0
  349. package/dist/ui/actions/CopyButton.js +2 -2
  350. package/dist/ui/actions/CopyButton.js.map +1 -1
  351. package/dist/ui/actions/DownloadMenu.js +11 -12
  352. package/dist/ui/actions/DownloadMenu.js.map +1 -1
  353. package/dist/ui/actions/SecretField.js +8 -4
  354. package/dist/ui/actions/SecretField.js.map +1 -1
  355. package/dist/ui/actions/SelectionActions.js +142 -0
  356. package/dist/ui/actions/SelectionActions.js.map +1 -0
  357. package/dist/ui/filters/DateRangeFilter.js +4 -3
  358. package/dist/ui/filters/DateRangeFilter.js.map +1 -1
  359. package/dist/ui/filters/QuickFilters.js +2 -2
  360. package/dist/ui/filters/QuickFilters.js.map +1 -1
  361. package/dist/ui/labels/tagOf.js +7 -0
  362. package/dist/ui/labels/tagOf.js.map +1 -0
  363. package/dist/ui/layout/CollapsibleSection.js +6 -2
  364. package/dist/ui/layout/CollapsibleSection.js.map +1 -1
  365. package/dist/ui/layout/ContentDialog.js +2 -2
  366. package/dist/ui/layout/ContentDialog.js.map +1 -1
  367. package/dist/ui/layout/ErrorPage.js +21 -7
  368. package/dist/ui/layout/ErrorPage.js.map +1 -1
  369. package/dist/ui/layout/FullPageLoader.js +2 -2
  370. package/dist/ui/layout/FullPageLoader.js.map +1 -1
  371. package/dist/ui/layout/PageHeading.js +2 -2
  372. package/dist/ui/layout/PageHeading.js.map +1 -1
  373. package/dist/ui/metrics/BarList.js +18 -13
  374. package/dist/ui/metrics/BarList.js.map +1 -1
  375. package/dist/ui/metrics/CalendarHeatmap.js +11 -6
  376. package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
  377. package/dist/ui/metrics/ChartTooltip.js +1 -1
  378. package/dist/ui/metrics/ChartTooltip.js.map +1 -1
  379. package/dist/ui/metrics/ColumnChart.js +59 -63
  380. package/dist/ui/metrics/ColumnChart.js.map +1 -1
  381. package/dist/ui/metrics/LineChart.js +117 -0
  382. package/dist/ui/metrics/LineChart.js.map +1 -0
  383. package/dist/ui/metrics/MetricCard.js +41 -8
  384. package/dist/ui/metrics/MetricCard.js.map +1 -1
  385. package/dist/ui/metrics/TableToggle.js +4 -2
  386. package/dist/ui/metrics/TableToggle.js.map +1 -1
  387. package/dist/ui/metrics/chartParts.js +93 -0
  388. package/dist/ui/metrics/chartParts.js.map +1 -0
  389. package/dist/ui/navigation/BackButton.js +2 -2
  390. package/dist/ui/navigation/BackButton.js.map +1 -1
  391. package/dist/ui/navigation/ColorSchemeToggle.js +6 -3
  392. package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
  393. package/dist/ui/navigation/FontSizeToggle.js +3 -2
  394. package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
  395. package/dist/ui/navigation/LanguageToggle.js +40 -19
  396. package/dist/ui/navigation/LanguageToggle.js.map +1 -1
  397. package/dist/ui/navigation/PageBreadcrumbs.js +2 -2
  398. package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
  399. package/dist/ui/timeline/GroupedTimeline.js +10 -8
  400. package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
  401. package/dist/upload/FileDropzone.js +5 -3
  402. package/dist/upload/FileDropzone.js.map +1 -1
  403. package/package.json +16 -7
@@ -7,8 +7,8 @@ import Stack from "@mui/material/Stack";
7
7
  import Typography from "@mui/material/Typography";
8
8
  import FilterListOffIcon from "@mui/icons-material/FilterListOff";
9
9
  import InboxOutlinedIcon from "@mui/icons-material/InboxOutlined";
10
- function EmptyState({ filtered = false, label, onClearFilters }) {
11
- const { t } = useToolkitTranslation("table");
10
+ function EmptyState({ filtered = false, label, onClearFilters, texts }) {
11
+ const { t } = useToolkitTranslation("table", texts);
12
12
  const Icon = filtered ? FilterListOffIcon : InboxOutlinedIcon;
13
13
  return /* @__PURE__ */ jsxs(Stack, { spacing: 1, sx: { height: "100%", px: 3, textAlign: "center", alignItems: "center", justifyContent: "center" }, children: [
14
14
  /* @__PURE__ */ jsx(Box, { sx: { color: "text.disabled", display: "flex" }, children: /* @__PURE__ */ jsx(Icon, { fontSize: "large" }) }),
@@ -1 +1 @@
1
- {"version":3,"file":"EmptyState.js","sources":["../../src/DataTable/EmptyState.jsx"],"sourcesContent":["// Qué decir cuando la tabla no tiene filas.\n//\n// Son dos situaciones distintas y confundirlas hace daño:\n// - sin filtros puestos, la lista vacía es el RESULTADO y se dice tal cual (label);\n// - con filtros puestos, la lista vacía NO dice nada del conjunto: dice que lo filtrado no existe dentro de él. Ahí\n// sí hay algo que hacer, y el botón lo hace.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport FilterListOffIcon from '@mui/icons-material/FilterListOff';\nimport InboxOutlinedIcon from '@mui/icons-material/InboxOutlined';\n\n/**\n * @param {{filtered?: boolean, label?: string, onClearFilters?: () => void}} props label ya traducido\n */\nexport default function EmptyState({filtered = false, label, onClearFilters}) {\n const {t} = useToolkitTranslation('table');\n const Icon = filtered ? FilterListOffIcon : InboxOutlinedIcon;\n\n return (\n <Stack spacing={1} sx={{height: '100%', px: 3, textAlign: 'center', alignItems: 'center', justifyContent: 'center'}}>\n <Box sx={{color: 'text.disabled', display: 'flex'}}>\n <Icon fontSize=\"large\" />\n </Box>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {filtered ? t('empty.filtered') : (label ?? t('empty.default'))}\n </Typography>\n {filtered && onClearFilters && (\n <Button size=\"small\" onClick={onClearFilters}>{t('empty.clearFilters')}</Button>\n )}\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAmBA,SAAwB,WAAW,EAAC,WAAW,OAAO,OAAO,kBAAiB;AAC1E,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AACzC,QAAM,OAAO,WAAW,oBAAoB;AAE5C,8BACK,OAAA,EAAM,SAAS,GAAG,IAAI,EAAC,QAAQ,QAAQ,IAAI,GAAG,WAAW,UAAU,YAAY,UAAU,gBAAgB,YACtG,UAAA;AAAA,IAAA,oBAAC,KAAA,EAAI,IAAI,EAAC,OAAO,iBAAiB,SAAS,OAAA,GACvC,UAAA,oBAAC,MAAA,EAAK,UAAS,QAAA,CAAQ,GAC3B;AAAA,IACA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAC7B,UAAA,WAAW,EAAE,gBAAgB,IAAK,SAAS,EAAE,eAAe,GACjE;AAAA,IACC,YAAY,kBACT,oBAAC,QAAA,EAAO,MAAK,SAAQ,SAAS,gBAAiB,UAAA,EAAE,oBAAoB,EAAA,CAAE;AAAA,EAAA,GAE/E;AAER;"}
1
+ {"version":3,"file":"EmptyState.js","sources":["../../src/DataTable/EmptyState.jsx"],"sourcesContent":["// Qué decir cuando la tabla no tiene filas.\n//\n// Son dos situaciones distintas y confundirlas hace daño:\n// - sin filtros puestos, la lista vacía es el RESULTADO y se dice tal cual (label);\n// - con filtros puestos, la lista vacía NO dice nada del conjunto: dice que lo filtrado no existe dentro de él. Ahí\n// sí hay algo que hacer, y el botón lo hace.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport FilterListOffIcon from '@mui/icons-material/FilterListOff';\nimport InboxOutlinedIcon from '@mui/icons-material/InboxOutlined';\n\n/**\n * @param {object} [props]\n * @param {boolean} [props.filtered]\n * @param {string} [props.label] ya traducido\n * @param {() => void} [props.onClearFilters]\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function EmptyState({filtered = false, label, onClearFilters, texts}) {\n const {t} = useToolkitTranslation('table', texts);\n const Icon = filtered ? FilterListOffIcon : InboxOutlinedIcon;\n\n return (\n <Stack spacing={1} sx={{height: '100%', px: 3, textAlign: 'center', alignItems: 'center', justifyContent: 'center'}}>\n <Box sx={{color: 'text.disabled', display: 'flex'}}>\n <Icon fontSize=\"large\" />\n </Box>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {filtered ? t('empty.filtered') : (label ?? t('empty.default'))}\n </Typography>\n {filtered && onClearFilters && (\n <Button size=\"small\" onClick={onClearFilters}>{t('empty.clearFilters')}</Button>\n )}\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAuBA,SAAwB,WAAW,EAAC,WAAW,OAAO,OAAO,gBAAgB,SAAQ;AACjF,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS,KAAK;AAChD,QAAM,OAAO,WAAW,oBAAoB;AAE5C,8BACK,OAAA,EAAM,SAAS,GAAG,IAAI,EAAC,QAAQ,QAAQ,IAAI,GAAG,WAAW,UAAU,YAAY,UAAU,gBAAgB,YACtG,UAAA;AAAA,IAAA,oBAAC,KAAA,EAAI,IAAI,EAAC,OAAO,iBAAiB,SAAS,OAAA,GACvC,UAAA,oBAAC,MAAA,EAAK,UAAS,QAAA,CAAQ,GAC3B;AAAA,IACA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAC7B,UAAA,WAAW,EAAE,gBAAgB,IAAK,SAAS,EAAE,eAAe,GACjE;AAAA,IACC,YAAY,kBACT,oBAAC,QAAA,EAAO,MAAK,SAAQ,SAAS,gBAAiB,UAAA,EAAE,oBAAoB,EAAA,CAAE;AAAA,EAAA,GAE/E;AAER;"}
@@ -8,8 +8,8 @@ import Typography from "@mui/material/Typography";
8
8
  import ClearAllIcon from "@mui/icons-material/ClearAll";
9
9
  import ScrollableRow from "../ui/layout/ScrollableRow.js";
10
10
  import Tag from "../ui/labels/Tag.js";
11
- function FilterChips({ chipConfig, filters, onSetFilter, onClearAll, quickActiveCount = 0 }) {
12
- const { t } = useToolkitTranslation("table");
11
+ function FilterChips({ chipConfig, filters, onSetFilter, onClearAll, quickActiveCount = 0, texts }) {
12
+ const { t } = useToolkitTranslation("table", texts);
13
13
  const chips = chipConfig.filter((chipDeclaration) => chipDeclaration.isActive(filters[chipDeclaration.key])).flatMap((chipDeclaration) => chipDeclaration.chipsFor(filters[chipDeclaration.key], onSetFilter));
14
14
  const activeCount = chips.length + quickActiveCount;
15
15
  if (activeCount === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"FilterChips.js","sources":["../../src/DataTable/FilterChips.jsx"],"sourcesContent":["// Los filtros activos, a la vista y desarmables de a uno.\n//\n// Un filtro que solo se ve como un icono encendido en el encabezado se olvida: alguien filtra, se va al detalle,\n// regresa y lee el total como si fuera el de todo. Por eso los filtros puestos se dicen con todas sus letras arriba de\n// la tabla.\n//\n// Van en UNA tira que se desliza (ScrollableRow), no envolviéndose en renglones que empujan la tabla fuera de la\n// pantalla. La cuenta y \"Limpiar todo\" van en su propio renglón, arriba, para no tener que deslizar hasta encontrarlo.\n// Sin filtros activos no se pinta nada (ni el hueco).\n//\n// Los accesos rápidos cuentan aunque no tengan chip aquí: su propio chip encendido ya dice qué está elegido.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport ClearAllIcon from '@mui/icons-material/ClearAll';\n\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport Tag from '@/ui/labels/Tag.jsx';\n\n/**\n * @param {object} props\n * @param {Array<object>} props.chipConfig lo que arma buildChipConfig\n * @param {object} props.filters\n * @param {(field: string, value: unknown) => void} props.onSetFilter\n * @param {() => void} props.onClearAll\n * @param {number} [props.quickActiveCount] accesos rápidos puestos que no tienen chip aquí\n */\nexport default function FilterChips({chipConfig, filters, onSetFilter, onClearAll, quickActiveCount = 0}) {\n const {t} = useToolkitTranslation('table');\n const chips = chipConfig\n .filter((chipDeclaration) => chipDeclaration.isActive(filters[chipDeclaration.key]))\n .flatMap((chipDeclaration) => chipDeclaration.chipsFor(filters[chipDeclaration.key], onSetFilter));\n\n const activeCount = chips.length + quickActiveCount;\n // guard clause: sin filtros no se pinta nada\n if (activeCount === 0) {\n return null;\n }\n\n return (\n <Box sx={{mb: 1.5, minWidth: 0}}>\n <Stack direction=\"row\" spacing={1} sx={{px: 0.25, alignItems: 'center', justifyContent: 'space-between'}}>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {t('chips.applied', {count: activeCount})}\n </Typography>\n <Button size=\"small\" color=\"error\" startIcon={<ClearAllIcon fontSize=\"small\" />} onClick={onClearAll}\n sx={{flexShrink: 0}}>\n {t('chips.clearAll')}\n </Button>\n </Stack>\n\n {chips.length > 0 && (\n <ScrollableRow>\n {chips.map(({id, label, onDelete}) => (\n <Tag key={id} label={label} color=\"primary\" onDelete={onDelete} sx={{flex: '0 0 auto'}} />\n ))}\n </ScrollableRow>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AA+BA,SAAwB,YAAY,EAAC,YAAY,SAAS,aAAa,YAAY,mBAAmB,KAAI;AACtG,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AACzC,QAAM,QAAQ,WACT,OAAO,CAAC,oBAAoB,gBAAgB,SAAS,QAAQ,gBAAgB,GAAG,CAAC,CAAC,EAClF,QAAQ,CAAC,oBAAoB,gBAAgB,SAAS,QAAQ,gBAAgB,GAAG,GAAG,WAAW,CAAC;AAErG,QAAM,cAAc,MAAM,SAAS;AAEnC,MAAI,gBAAgB,GAAG;AACnB,WAAO;AAAA,EACX;AAEA,SACI,qBAAC,OAAI,IAAI,EAAC,IAAI,KAAK,UAAU,KACzB,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,IAAI,MAAM,YAAY,UAAU,gBAAgB,mBACpF,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,QAAM,MACtD,UAAA,EAAE,iBAAiB,EAAC,OAAO,YAAA,CAAY,GAC5C;AAAA,MACA;AAAA,QAAC;AAAA,QAAA;AAAA,UAAO,MAAK;AAAA,UAAQ,OAAM;AAAA,UAAQ,WAAW,oBAAC,cAAA,EAAa,UAAS,QAAA,CAAQ;AAAA,UAAI,SAAS;AAAA,UAClF,IAAI,EAAC,YAAY,EAAA;AAAA,UACpB,YAAE,gBAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IACvB,GACJ;AAAA,IAEC,MAAM,SAAS,KACZ,oBAAC,eAAA,EACI,UAAA,MAAM,IAAI,CAAC,EAAC,IAAI,OAAO,SAAA,0BACnB,KAAA,EAAa,OAAc,OAAM,WAAU,UAAoB,IAAI,EAAC,MAAM,WAAA,KAAjE,EAA8E,CAC3F,EAAA,CACL;AAAA,EAAA,GAER;AAER;"}
1
+ {"version":3,"file":"FilterChips.js","sources":["../../src/DataTable/FilterChips.jsx"],"sourcesContent":["// Los filtros activos, a la vista y desarmables de a uno.\n//\n// Un filtro que solo se ve como un icono encendido en el encabezado se olvida: alguien filtra, se va al detalle,\n// regresa y lee el total como si fuera el de todo. Por eso los filtros puestos se dicen con todas sus letras arriba de\n// la tabla.\n//\n// Van en UNA tira que se desliza (ScrollableRow), no envolviéndose en renglones que empujan la tabla fuera de la\n// pantalla. La cuenta y \"Limpiar todo\" van en su propio renglón, arriba, para no tener que deslizar hasta encontrarlo.\n// Sin filtros activos no se pinta nada (ni el hueco).\n//\n// Los accesos rápidos cuentan aunque no tengan chip aquí: su propio chip encendido ya dice qué está elegido.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport ClearAllIcon from '@mui/icons-material/ClearAll';\n\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport Tag from '@/ui/labels/Tag.jsx';\n\n/**\n * @param {object} props\n * @param {Array<object>} props.chipConfig lo que arma buildChipConfig\n * @param {object} props.filters\n * @param {(field: string, value: unknown) => void} props.onSetFilter\n * @param {() => void} props.onClearAll\n * @param {number} [props.quickActiveCount] accesos rápidos puestos que no tienen chip aquí\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function FilterChips({chipConfig, filters, onSetFilter, onClearAll, quickActiveCount = 0, texts}) {\n const {t} = useToolkitTranslation('table', texts);\n const chips = chipConfig\n .filter((chipDeclaration) => chipDeclaration.isActive(filters[chipDeclaration.key]))\n .flatMap((chipDeclaration) => chipDeclaration.chipsFor(filters[chipDeclaration.key], onSetFilter));\n\n const activeCount = chips.length + quickActiveCount;\n // guard clause: sin filtros no se pinta nada\n if (activeCount === 0) {\n return null;\n }\n\n return (\n <Box sx={{mb: 1.5, minWidth: 0}}>\n <Stack direction=\"row\" spacing={1} sx={{px: 0.25, alignItems: 'center', justifyContent: 'space-between'}}>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {t('chips.applied', {count: activeCount})}\n </Typography>\n <Button size=\"small\" color=\"error\" startIcon={<ClearAllIcon fontSize=\"small\" />} onClick={onClearAll}\n sx={{flexShrink: 0}}>\n {t('chips.clearAll')}\n </Button>\n </Stack>\n\n {chips.length > 0 && (\n <ScrollableRow>\n {chips.map(({id, label, onDelete}) => (\n <Tag key={id} label={label} color=\"primary\" onDelete={onDelete} sx={{flex: '0 0 auto'}} />\n ))}\n </ScrollableRow>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAgCA,SAAwB,YAAY,EAAC,YAAY,SAAS,aAAa,YAAY,mBAAmB,GAAG,SAAQ;AAC7G,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS,KAAK;AAChD,QAAM,QAAQ,WACT,OAAO,CAAC,oBAAoB,gBAAgB,SAAS,QAAQ,gBAAgB,GAAG,CAAC,CAAC,EAClF,QAAQ,CAAC,oBAAoB,gBAAgB,SAAS,QAAQ,gBAAgB,GAAG,GAAG,WAAW,CAAC;AAErG,QAAM,cAAc,MAAM,SAAS;AAEnC,MAAI,gBAAgB,GAAG;AACnB,WAAO;AAAA,EACX;AAEA,SACI,qBAAC,OAAI,IAAI,EAAC,IAAI,KAAK,UAAU,KACzB,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,IAAI,MAAM,YAAY,UAAU,gBAAgB,mBACpF,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,QAAM,MACtD,UAAA,EAAE,iBAAiB,EAAC,OAAO,YAAA,CAAY,GAC5C;AAAA,MACA;AAAA,QAAC;AAAA,QAAA;AAAA,UAAO,MAAK;AAAA,UAAQ,OAAM;AAAA,UAAQ,WAAW,oBAAC,cAAA,EAAa,UAAS,QAAA,CAAQ;AAAA,UAAI,SAAS;AAAA,UAClF,IAAI,EAAC,YAAY,EAAA;AAAA,UACpB,YAAE,gBAAgB;AAAA,QAAA;AAAA,MAAA;AAAA,IACvB,GACJ;AAAA,IAEC,MAAM,SAAS,KACZ,oBAAC,eAAA,EACI,UAAA,MAAM,IAAI,CAAC,EAAC,IAAI,OAAO,SAAA,0BACnB,KAAA,EAAa,OAAc,OAAM,WAAU,UAAoB,IAAI,EAAC,MAAM,WAAA,KAAjE,EAA8E,CAC3F,EAAA,CACL;AAAA,EAAA,GAER;AAER;"}
@@ -5,7 +5,7 @@ import Autocomplete from "@mui/material/Autocomplete";
5
5
  import Stack from "@mui/material/Stack";
6
6
  import TextField from "@mui/material/TextField";
7
7
  import Typography from "@mui/material/Typography";
8
- import { formatNumber } from "../formatting.js";
8
+ import { useFormatters } from "../toolkit/useFormatters.js";
9
9
  const RENDER_LIMIT = 100;
10
10
  function filterPages(pageNumbers, { inputValue }) {
11
11
  const typedText = inputValue.trim();
@@ -16,8 +16,9 @@ function filterPages(pageNumbers, { inputValue }) {
16
16
  const containing = pageNumbers.filter((pageNumber) => !String(pageNumber).startsWith(typedText) && String(pageNumber).includes(typedText));
17
17
  return [...startingWith, ...containing].slice(0, RENDER_LIMIT);
18
18
  }
19
- function GoToPage({ page, pageCount, onChange }) {
20
- const { t } = useToolkitTranslation("table");
19
+ function GoToPage({ page, pageCount, onChange, texts }) {
20
+ const { t } = useToolkitTranslation("table", texts);
21
+ const { formatNumber } = useFormatters();
21
22
  if (pageCount <= 1) {
22
23
  return null;
23
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GoToPage.js","sources":["../../src/DataTable/GoToPage.jsx"],"sourcesContent":["// \"Ir a la página N\", como un selector con búsqueda.\n//\n// Con cientos de páginas, llegar a la 137 a punta de flechita no es navegar. Un Autocomplete dice las tres cosas sin\n// explicarlas: se abre y se ve la lista, se teclea y se filtra, se elige y ya.\n//\n// Tres detalles:\n// 1. la lista se recorta a RENDER_LIMIT opciones en el DOM; lo demás se alcanza tecleando;\n// 2. filtra por PREFIJO primero (teclear 13 da 13, 130, 131...) y luego por \"contiene\";\n// 3. con una sola página no se pinta: sería un control que no lleva a ningún lado.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {formatNumber} from '@/formatting.js';\n\n// tope del DOM, no de la navegación\nconst RENDER_LIMIT = 100;\n\nfunction filterPages(pageNumbers, {inputValue}) {\n const typedText = inputValue.trim();\n if (!typedText) {\n return pageNumbers.slice(0, RENDER_LIMIT);\n }\n const startingWith = pageNumbers.filter((pageNumber) => String(pageNumber).startsWith(typedText));\n const containing = pageNumbers.filter((pageNumber) => !String(pageNumber).startsWith(typedText) && String(pageNumber).includes(typedText));\n return [...startingWith, ...containing].slice(0, RENDER_LIMIT);\n}\n\n/**\n * @param {{page: number, pageCount: number, onChange: (page: number) => void}} props page cuenta desde 0\n */\nexport default function GoToPage({page, pageCount, onChange}) {\n const {t} = useToolkitTranslation('table');\n\n // guard clause: con una página no hay a dónde ir\n if (pageCount <= 1) {\n return null;\n }\n\n // la persona cuenta desde 1 y el estado de la tabla desde 0: la conversión vive AQUÍ y en ningún otro lado\n const pageNumbers = Array.from({length: pageCount}, (unused, position) => position + 1);\n\n return (\n <Stack direction=\"row\" spacing={0.75} sx={{alignItems: 'baseline'}}>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{t('goToPage.page')}</Typography>\n <Autocomplete\n size=\"small\"\n disableClearable\n openOnFocus\n autoHighlight\n options={pageNumbers}\n filterOptions={filterPages}\n value={page + 1}\n isOptionEqualToValue={(option, selectedValue) => option === selectedValue}\n getOptionLabel={(option) => String(option)}\n onChange={(event, selectedPageNumber) => {\n if (selectedPageNumber !== null && selectedPageNumber - 1 !== page) {\n onChange(selectedPageNumber - 1);\n }\n }}\n sx={{width: 78, '& .MuiAutocomplete-endAdornment': {right: 0}}}\n slotProps={{listbox: {sx: {maxHeight: 240}}}}\n renderInput={(params) => (\n <TextField {...params} variant=\"standard\"\n slotProps={{htmlInput: {...params.inputProps, inputMode: 'numeric', 'aria-label': t('goToPage.ariaLabel')}}} />\n )}\n />\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>\n {t('goToPage.of', {count: pageCount, total: formatNumber(pageCount)})}\n </Typography>\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAoBA,MAAM,eAAe;AAErB,SAAS,YAAY,aAAa,EAAC,cAAa;AAC5C,QAAM,YAAY,WAAW,KAAA;AAC7B,MAAI,CAAC,WAAW;AACZ,WAAO,YAAY,MAAM,GAAG,YAAY;AAAA,EAC5C;AACA,QAAM,eAAe,YAAY,OAAO,CAAC,eAAe,OAAO,UAAU,EAAE,WAAW,SAAS,CAAC;AAChG,QAAM,aAAa,YAAY,OAAO,CAAC,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,SAAS,KAAK,OAAO,UAAU,EAAE,SAAS,SAAS,CAAC;AACzI,SAAO,CAAC,GAAG,cAAc,GAAG,UAAU,EAAE,MAAM,GAAG,YAAY;AACjE;AAKA,SAAwB,SAAS,EAAC,MAAM,WAAW,YAAW;AAC1D,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AAGzC,MAAI,aAAa,GAAG;AAChB,WAAO;AAAA,EACX;AAGA,QAAM,cAAc,MAAM,KAAK,EAAC,QAAQ,UAAA,GAAY,CAAC,QAAQ,aAAa,WAAW,CAAC;AAEtF,SACI,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,MAAM,IAAI,EAAC,YAAY,WAAA,GACnD,UAAA;AAAA,IAAA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,QAAM,MAAE,UAAA,EAAE,eAAe,EAAA,CAAE;AAAA,IAC9E;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,MAAK;AAAA,QACL,kBAAgB;AAAA,QAChB,aAAW;AAAA,QACX,eAAa;AAAA,QACb,SAAS;AAAA,QACT,eAAe;AAAA,QACf,OAAO,OAAO;AAAA,QACd,sBAAsB,CAAC,QAAQ,kBAAkB,WAAW;AAAA,QAC5D,gBAAgB,CAAC,WAAW,OAAO,MAAM;AAAA,QACzC,UAAU,CAAC,OAAO,uBAAuB;AACrC,cAAI,uBAAuB,QAAQ,qBAAqB,MAAM,MAAM;AAChE,qBAAS,qBAAqB,CAAC;AAAA,UACnC;AAAA,QACJ;AAAA,QACA,IAAI,EAAC,OAAO,IAAI,mCAAmC,EAAC,OAAO,IAAC;AAAA,QAC5D,WAAW,EAAC,SAAS,EAAC,IAAI,EAAC,WAAW,IAAA,IAAI;AAAA,QAC1C,aAAa,CAAC,WACV;AAAA,UAAC;AAAA,UAAA;AAAA,YAAW,GAAG;AAAA,YAAQ,SAAQ;AAAA,YACpB,WAAW,EAAC,WAAW,EAAC,GAAG,OAAO,YAAY,WAAW,WAAW,cAAc,EAAE,oBAAoB,IAAC;AAAA,UAAC;AAAA,QAAA;AAAA,MAAG;AAAA,IAAA;AAAA,wBAG/H,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,QAAM,MACpD,UAAA,EAAE,eAAe,EAAC,OAAO,WAAW,OAAO,aAAa,SAAS,EAAA,CAAE,EAAA,CACxE;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"GoToPage.js","sources":["../../src/DataTable/GoToPage.jsx"],"sourcesContent":["// \"Ir a la página N\", como un selector con búsqueda.\n//\n// Con cientos de páginas, llegar a la 137 a punta de flechita no es navegar. Un Autocomplete dice las tres cosas sin\n// explicarlas: se abre y se ve la lista, se teclea y se filtra, se elige y ya.\n//\n// Tres detalles:\n// 1. la lista se recorta a RENDER_LIMIT opciones en el DOM; lo demás se alcanza tecleando;\n// 2. filtra por PREFIJO primero (teclear 13 da 13, 130, 131...) y luego por \"contiene\";\n// 3. con una sola página no se pinta: sería un control que no lleva a ningún lado.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\n\n// tope del DOM, no de la navegación\nconst RENDER_LIMIT = 100;\n\nfunction filterPages(pageNumbers, {inputValue}) {\n const typedText = inputValue.trim();\n if (!typedText) {\n return pageNumbers.slice(0, RENDER_LIMIT);\n }\n const startingWith = pageNumbers.filter((pageNumber) => String(pageNumber).startsWith(typedText));\n const containing = pageNumbers.filter((pageNumber) => !String(pageNumber).startsWith(typedText) && String(pageNumber).includes(typedText));\n return [...startingWith, ...containing].slice(0, RENDER_LIMIT);\n}\n\n/**\n * @param {object} props\n * @param {number} props.page cuenta desde 0\n * @param {number} props.pageCount\n * @param {(page: number) => void} props.onChange\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function GoToPage({page, pageCount, onChange, texts}) {\n const {t} = useToolkitTranslation('table', texts);\n const {formatNumber} = useFormatters();\n\n // guard clause: con una página no hay a dónde ir\n if (pageCount <= 1) {\n return null;\n }\n\n // la persona cuenta desde 1 y el estado de la tabla desde 0: la conversión vive AQUÍ y en ningún otro lado\n const pageNumbers = Array.from({length: pageCount}, (unused, position) => position + 1);\n\n return (\n <Stack direction=\"row\" spacing={0.75} sx={{alignItems: 'baseline'}}>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{t('goToPage.page')}</Typography>\n <Autocomplete\n size=\"small\"\n disableClearable\n openOnFocus\n autoHighlight\n options={pageNumbers}\n filterOptions={filterPages}\n value={page + 1}\n isOptionEqualToValue={(option, selectedValue) => option === selectedValue}\n getOptionLabel={(option) => String(option)}\n onChange={(event, selectedPageNumber) => {\n if (selectedPageNumber !== null && selectedPageNumber - 1 !== page) {\n onChange(selectedPageNumber - 1);\n }\n }}\n sx={{width: 78, '& .MuiAutocomplete-endAdornment': {right: 0}}}\n slotProps={{listbox: {sx: {maxHeight: 240}}}}\n renderInput={(params) => (\n <TextField {...params} variant=\"standard\"\n slotProps={{htmlInput: {...params.inputProps, inputMode: 'numeric', 'aria-label': t('goToPage.ariaLabel')}}} />\n )}\n />\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>\n {t('goToPage.of', {count: pageCount, total: formatNumber(pageCount)})}\n </Typography>\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAoBA,MAAM,eAAe;AAErB,SAAS,YAAY,aAAa,EAAC,cAAa;AAC5C,QAAM,YAAY,WAAW,KAAA;AAC7B,MAAI,CAAC,WAAW;AACZ,WAAO,YAAY,MAAM,GAAG,YAAY;AAAA,EAC5C;AACA,QAAM,eAAe,YAAY,OAAO,CAAC,eAAe,OAAO,UAAU,EAAE,WAAW,SAAS,CAAC;AAChG,QAAM,aAAa,YAAY,OAAO,CAAC,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,SAAS,KAAK,OAAO,UAAU,EAAE,SAAS,SAAS,CAAC;AACzI,SAAO,CAAC,GAAG,cAAc,GAAG,UAAU,EAAE,MAAM,GAAG,YAAY;AACjE;AASA,SAAwB,SAAS,EAAC,MAAM,WAAW,UAAU,SAAQ;AACjE,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS,KAAK;AAChD,QAAM,EAAC,aAAA,IAAgB,cAAA;AAGvB,MAAI,aAAa,GAAG;AAChB,WAAO;AAAA,EACX;AAGA,QAAM,cAAc,MAAM,KAAK,EAAC,QAAQ,UAAA,GAAY,CAAC,QAAQ,aAAa,WAAW,CAAC;AAEtF,SACI,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,MAAM,IAAI,EAAC,YAAY,WAAA,GACnD,UAAA;AAAA,IAAA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,QAAM,MAAE,UAAA,EAAE,eAAe,EAAA,CAAE;AAAA,IAC9E;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,MAAK;AAAA,QACL,kBAAgB;AAAA,QAChB,aAAW;AAAA,QACX,eAAa;AAAA,QACb,SAAS;AAAA,QACT,eAAe;AAAA,QACf,OAAO,OAAO;AAAA,QACd,sBAAsB,CAAC,QAAQ,kBAAkB,WAAW;AAAA,QAC5D,gBAAgB,CAAC,WAAW,OAAO,MAAM;AAAA,QACzC,UAAU,CAAC,OAAO,uBAAuB;AACrC,cAAI,uBAAuB,QAAQ,qBAAqB,MAAM,MAAM;AAChE,qBAAS,qBAAqB,CAAC;AAAA,UACnC;AAAA,QACJ;AAAA,QACA,IAAI,EAAC,OAAO,IAAI,mCAAmC,EAAC,OAAO,IAAC;AAAA,QAC5D,WAAW,EAAC,SAAS,EAAC,IAAI,EAAC,WAAW,IAAA,IAAI;AAAA,QAC1C,aAAa,CAAC,WACV;AAAA,UAAC;AAAA,UAAA;AAAA,YAAW,GAAG;AAAA,YAAQ,SAAQ;AAAA,YACpB,WAAW,EAAC,WAAW,EAAC,GAAG,OAAO,YAAY,WAAW,WAAW,cAAc,EAAE,oBAAoB,IAAC;AAAA,UAAC;AAAA,QAAA;AAAA,MAAG;AAAA,IAAA;AAAA,wBAG/H,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,QAAM,MACpD,UAAA,EAAE,eAAe,EAAC,OAAO,WAAW,OAAO,aAAa,SAAS,EAAA,CAAE,EAAA,CACxE;AAAA,EAAA,GACJ;AAER;"}
@@ -1,30 +1,33 @@
1
1
  "use client";
2
- import { jsxs, jsx } from "react/jsx-runtime";
3
- import { useState, useRef, useMemo, useEffect } from "react";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useRef, useMemo, useEffect, useState, useCallback } from "react";
4
4
  import { useMachine } from "@xstate/react";
5
5
  import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
6
6
  import Alert from "@mui/material/Alert";
7
7
  import Box from "@mui/material/Box";
8
8
  import Button from "@mui/material/Button";
9
- import InputAdornment from "@mui/material/InputAdornment";
9
+ import "@mui/material/InputAdornment";
10
10
  import Paper from "@mui/material/Paper";
11
11
  import Stack from "@mui/material/Stack";
12
- import TextField from "@mui/material/TextField";
12
+ import "@mui/material/TextField";
13
13
  import Typography from "@mui/material/Typography";
14
- import SearchIcon from "@mui/icons-material/Search";
15
- import { DataGrid, GridPagination } from "@mui/x-data-grid";
14
+ import { useTheme } from "@mui/material/styles";
15
+ import "@mui/icons-material/Search";
16
+ import { GRID_CHECKBOX_SELECTION_COL_DEF, DataGrid, GridPagination, GRID_CHECKBOX_SELECTION_FIELD } from "@mui/x-data-grid";
16
17
  import { enUS, esES } from "@mui/x-data-grid/locales";
17
18
  import EmptyState from "./EmptyState.js";
18
19
  import FilterChips from "./FilterChips.js";
19
20
  import GoToPage from "./GoToPage.js";
20
21
  import { TableFilterContext } from "./filterContext.js";
22
+ import { EMPTY_ROW_SELECTION, checkedIdsOf, rowSelectionOf, canSelectAllMatching, selectionAfterCheck } from "./rowSelection.js";
21
23
  import { tableMachine, buildTableActors } from "./tableMachine.js";
22
24
  import { useDebouncedFilter } from "./useDebouncedFilter.js";
23
25
  import { useTableState, isFilterActive, FILTER_KINDS } from "./useTableState.js";
24
- import BarActions from "../appShell/BarActions.js";
25
- import { BAR_CAPSULE_HEIGHT_PIXELS } from "../appShell/BarCapsule.js";
26
+ import BarSearch from "../appShell/BarSearch.js";
26
27
  import { PHONE_MEDIA_QUERY } from "../appShell/shellSizes.js";
27
- import { formatNumber } from "../formatting.js";
28
+ import SelectionActions, { selectionFloatsOnPhone, SELECTION_PHONE_LAYOUT } from "../ui/actions/SelectionActions.js";
29
+ import ComponentTexts from "../toolkit/ComponentTexts.js";
30
+ import { useFormatters } from "../toolkit/useFormatters.js";
28
31
  import { materialBackgroundOf } from "../theme/glassSurface.js";
29
32
  import QuickFilters from "../ui/filters/QuickFilters.js";
30
33
  import Tag from "../ui/labels/Tag.js";
@@ -44,34 +47,43 @@ const FOOTER_HEIGHT_PIXELS = 52;
44
47
  const GRID_BORDER_PIXELS = 2;
45
48
  const HEIGHT_SLACK_PIXELS = 2;
46
49
  const DATA_GRID_DENSITY = "compact";
47
- const SEARCH_BOX_MIN_WIDTH_PIXELS = 400;
48
- const SEARCH_IN_BAR_WIDTH_PIXELS = 380;
49
50
  const STRIPED_ROW_CLASS = "server-table-row--striped";
50
51
  const FIXED_CELL_Z_INDEX = 30;
51
52
  const FIXED_HEADER_Z_INDEX = 40;
52
- function fixedColumnSxOf(theme, field) {
53
+ const CHECKBOX_COLUMN_WIDTH_PIXELS = GRID_CHECKBOX_SELECTION_COL_DEF.width;
54
+ function fixedColumnSxOf(theme, field, { withCheckboxColumn = false } = {}) {
53
55
  const { palette } = theme.vars ?? theme;
54
56
  const paper = palette.background.paper;
55
57
  const divider = `1px solid ${palette.divider}`;
56
58
  const ownBandHeader = `.MuiDataGrid-columnHeader[data-fields="|-${field}-|"]`;
57
59
  const isMaterialContent = Boolean(theme.toolkit?.glass?.surfaces);
58
60
  const stickyBackground = { backgroundColor: "inherit", ...isMaterialContent ? materialBackgroundOf(theme, { thick: true }) : { backgroundImage: `linear-gradient(${paper}, ${paper})` } };
61
+ const headerBackground = isMaterialContent ? materialBackgroundOf(theme, { thick: true }) : { backgroundColor: paper };
62
+ const rowColorLayer = { content: '""', position: "absolute", inset: 0, zIndex: -1, backgroundColor: "inherit" };
63
+ const fixedLeft = withCheckboxColumn ? CHECKBOX_COLUMN_WIDTH_PIXELS : 0;
64
+ const checkboxCell = `.MuiDataGrid-cell[data-field="${GRID_CHECKBOX_SELECTION_FIELD}"]`;
65
+ const checkboxHeader = `.MuiDataGrid-columnHeader[data-field="${GRID_CHECKBOX_SELECTION_FIELD}"]`;
59
66
  return {
60
67
  "& :where(.MuiDataGrid-row)": { backgroundColor: isMaterialContent ? "transparent" : paper },
61
68
  [`& .MuiDataGrid-cell[data-field="${field}"]`]: {
62
69
  position: "sticky",
63
- left: 0,
70
+ left: fixedLeft,
64
71
  zIndex: FIXED_CELL_Z_INDEX,
65
72
  borderRight: divider,
66
73
  ...stickyBackground,
67
- "&::before": { content: '""', position: "absolute", inset: 0, zIndex: -1, backgroundColor: "inherit" }
74
+ "&::before": rowColorLayer
68
75
  },
69
- [`& :where(.MuiDataGrid-columnHeader[data-field="${field}"], ${ownBandHeader})`]: isMaterialContent ? materialBackgroundOf(theme, { thick: true }) : { backgroundColor: paper },
76
+ [`& :where(.MuiDataGrid-columnHeader[data-field="${field}"], ${ownBandHeader})`]: headerBackground,
70
77
  [`& .MuiDataGrid-columnHeader[data-field="${field}"], & ${ownBandHeader}`]: {
71
78
  position: "sticky",
72
- left: 0,
79
+ left: fixedLeft,
73
80
  zIndex: FIXED_HEADER_Z_INDEX,
74
81
  borderRight: divider
82
+ },
83
+ ...withCheckboxColumn && {
84
+ [`& ${checkboxCell}`]: { position: "sticky", left: 0, zIndex: FIXED_CELL_Z_INDEX, ...stickyBackground, "&::before": rowColorLayer },
85
+ [`& :where(${checkboxHeader})`]: headerBackground,
86
+ [`& ${checkboxHeader}`]: { position: "sticky", left: 0, zIndex: FIXED_HEADER_Z_INDEX }
75
87
  }
76
88
  };
77
89
  }
@@ -155,18 +167,31 @@ function ServerTable({
155
167
  gridSx,
156
168
  maxGridHeight = null,
157
169
  ariaLabel,
158
- fixedColumn
170
+ fixedColumn,
171
+ selectionActions = [],
172
+ isRowSelectable,
173
+ allowSelectAllMatching = true,
174
+ onSelectionChange,
175
+ selectionPhoneLayout = SELECTION_PHONE_LAYOUT.top,
176
+ onQueryChange,
177
+ onLoaded,
178
+ onLoadError,
179
+ gridProps,
180
+ texts
159
181
  }) {
160
- const { t, language, changeLanguage } = useToolkitTranslation("table");
182
+ const { t, language } = useToolkitTranslation("table", texts);
183
+ const { formatNumber } = useFormatters();
184
+ const theme = useTheme();
161
185
  const tableState = useTableState({ prefix, filterDefaults, filterKinds, defaultPageSize });
162
186
  const { filters, sortOrders, page, pageSize } = tableState;
163
- const [isSearchFocused, setIsSearchFocused] = useState(false);
164
187
  const [searchDraft, setSearchDraft] = useDebouncedFilter(
165
188
  searchField ? filters[searchField] : "",
166
189
  (value) => searchField && tableState.setFilter(searchField, value)
167
190
  );
168
191
  const latestFetchPage = useRef(fetchPage);
169
192
  latestFetchPage.current = fetchPage;
193
+ const latestEvents = useRef({ onQueryChange, onLoaded, onLoadError });
194
+ latestEvents.current = { onQueryChange, onLoaded, onLoadError };
170
195
  const [tableSnapshot, sendToTable] = useMachine(
171
196
  tableMachine.provide({ actors: buildTableActors((params, signal) => latestFetchPage.current(params, signal)) })
172
197
  );
@@ -179,8 +204,20 @@ function ServerTable({
179
204
  if (!queryKey || !enabled) {
180
205
  return;
181
206
  }
207
+ latestEvents.current.onQueryChange?.(requestParams);
182
208
  sendToTable({ type: "FETCH", params: requestParams });
183
209
  }, [requestFingerprint]);
210
+ const isReady = tableSnapshot.matches("ready");
211
+ useEffect(() => {
212
+ if (isReady) {
213
+ latestEvents.current.onLoaded?.({ rows, total, facets, capped });
214
+ }
215
+ }, [isReady, rows]);
216
+ useEffect(() => {
217
+ if (error) {
218
+ latestEvents.current.onLoadError?.(error);
219
+ }
220
+ }, [error]);
184
221
  const toggleSort = (column) => {
185
222
  const currentSort = sortOrders.find((sortOrder) => sortOrder.column === column);
186
223
  if (!currentSort) {
@@ -191,6 +228,55 @@ function ServerTable({
191
228
  tableState.setSort([]);
192
229
  }
193
230
  };
231
+ const isSelectable = selectionActions.length > 0 || Boolean(onSelectionChange);
232
+ const [rowSelection, setRowSelection] = useState(EMPTY_ROW_SELECTION);
233
+ const idOf = useCallback((row) => getRowId ? getRowId(row) : row.id, [getRowId]);
234
+ const pageRowsById = useMemo(() => new Map(visibleRows.map((row) => [idOf(row), row])), [visibleRows, idOf]);
235
+ const selectablePageIds = useMemo(
236
+ () => visibleRows.filter((row) => !isRowSelectable || isRowSelectable(row)).map(idOf),
237
+ [visibleRows, isRowSelectable, idOf]
238
+ );
239
+ const checkedIds = useMemo(() => checkedIdsOf(rowSelection, selectablePageIds), [rowSelection, selectablePageIds]);
240
+ const selection = rowSelectionOf(rowSelection, { total, capped, filters });
241
+ const updateSelection = (nextSelection) => {
242
+ setRowSelection(nextSelection);
243
+ onSelectionChange?.(rowSelectionOf(nextSelection, { total, capped, filters }));
244
+ };
245
+ const clearSelection = () => updateSelection(EMPTY_ROW_SELECTION);
246
+ const selectionResetKey = JSON.stringify({ queryKey, filters });
247
+ const lastSelectionResetKey = useRef(selectionResetKey);
248
+ useEffect(() => {
249
+ if (lastSelectionResetKey.current === selectionResetKey) {
250
+ return;
251
+ }
252
+ lastSelectionResetKey.current = selectionResetKey;
253
+ if (rowSelection !== EMPTY_ROW_SELECTION) {
254
+ clearSelection();
255
+ }
256
+ }, [selectionResetKey]);
257
+ const handleGridSelectionChange = (model) => {
258
+ const nextSelection = selectionAfterCheck(rowSelection, model.ids, pageRowsById, selectablePageIds, {
259
+ allMatchingOnWholePage: allowSelectAllMatching,
260
+ total,
261
+ capped
262
+ });
263
+ if (nextSelection !== rowSelection) {
264
+ updateSelection(nextSelection);
265
+ }
266
+ };
267
+ const hasSelection = isSelectable && selection.count > 0;
268
+ const floatsSelectionOnPhone = selectionFloatsOnPhone(theme);
269
+ const selectionCountText = selection.isCapped ? t("selection.allMatchingCapped") : t("selection.count", { count: selection.count, total: formatNumber(selection.count) });
270
+ const selectionHelpers = { clearSelection, refresh: () => sendToTable({ type: "FETCH", params: requestParams }) };
271
+ const selectionActionItems = selectionActions.map(({ key, label, icon, color, isDisabled, onClick }) => ({
272
+ key,
273
+ label,
274
+ icon,
275
+ color,
276
+ disabled: isDisabled?.(selection) ?? false,
277
+ onClick: () => onClick(selection, selectionHelpers)
278
+ }));
279
+ const offersAllMatching = isSelectable && allowSelectAllMatching && canSelectAllMatching(rowSelection, selectablePageIds, { total, capped });
194
280
  const hasActiveFilters = Object.entries(filters).some(([field, value]) => field !== searchField && isFilterActive(filterKinds[field], value));
195
281
  const quickActiveCount = quickFilters.filter((quickFilter) => !chipConfig.some((chipDeclaration) => chipDeclaration.key === quickFilter.field) && JSON.stringify(filters[quickFilter.field] ?? "") !== JSON.stringify(filterDefaults[quickFilter.field] ?? "")).length;
196
282
  const tableFilterValue = useMemo(() => ({
@@ -221,71 +307,25 @@ function ServerTable({
221
307
  const formattedTotal = formatNumber(total);
222
308
  const countText = countLabel ? countLabel(total) : t("count.records", { count: total, total: formattedTotal });
223
309
  const searchText = searchPlaceholder ?? t("search.placeholder");
224
- const isSearchCompact = searchInBar && collapseSearchInBar && !isSearchFocused && !searchDraft;
225
- const searchBox = searchField && /* @__PURE__ */ jsx(
226
- TextField,
227
- {
228
- type: "search",
229
- size: "small",
230
- value: searchDraft,
231
- onChange: (event) => setSearchDraft(event.target.value),
232
- onFocus: () => setIsSearchFocused(true),
233
- onBlur: () => setIsSearchFocused(false),
234
- onKeyDown: (event) => {
235
- if (event.key === "Escape") {
236
- setSearchDraft("");
237
- event.currentTarget.querySelector("input")?.blur();
238
- }
239
- },
240
- placeholder: searchText,
241
- sx: { minWidth: { xs: 0, md: SEARCH_BOX_MIN_WIDTH_PIXELS } },
242
- slotProps: {
243
- input: { startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(SearchIcon, { fontSize: "small" }) }) },
244
- htmlInput: { "aria-label": searchText }
245
- }
246
- }
247
- );
248
- return /* @__PURE__ */ jsxs(TableFilterContext, { value: tableFilterValue, children: [
310
+ return /* @__PURE__ */ jsx(ComponentTexts, { namespace: "table", texts, children: /* @__PURE__ */ jsxs(TableFilterContext, { value: tableFilterValue, children: [
249
311
  /* @__PURE__ */ jsxs(Stack, { direction: { xs: "column", md: "row" }, spacing: 2, sx: { mb: 1.5, alignItems: { md: "center" } }, children: [
250
- searchBox && !searchInBar && searchBox,
251
- searchBox && searchInBar && /* @__PURE__ */ jsx(
252
- BarActions,
312
+ searchField && /* @__PURE__ */ jsx(BarSearch, { value: searchDraft, onChange: setSearchDraft, placeholder: searchText, inBar: searchInBar, collapsible: collapseSearchInBar }),
313
+ hasSelection && /* @__PURE__ */ jsx(
314
+ SelectionActions,
253
315
  {
254
- label: searchText,
255
- bare: true,
256
- order: 0,
257
- sx: (theme) => ({
258
- // en el teléfono flota arriba junto a las demás cápsulas. Su lugar SIEMPRE ocupa lo libre del
259
- // renglón y la caja va pegada a la derecha: así se abre de derecha a izquierda y regresa igual
260
- // (el ancho pasa de 48 px a 100 % con transición). OJO: antes el lugar brincaba de golpe
261
- // (flex-grow no se anima) y la caja crecía de izquierda a derecha; se veía muy mal. El hueco de
262
- // la izquierda deja pasar el dedo a lo de abajo; solo la caja lo recibe
263
- [PHONE_MEDIA_QUERY]: {
264
- flexGrow: 1,
265
- minWidth: 0,
266
- justifyContent: "flex-end",
267
- pointerEvents: "none",
268
- "& .MuiTextField-root": { pointerEvents: "auto" }
269
- },
270
- "& .MuiTextField-root": {
271
- width: isSearchCompact ? BAR_CAPSULE_HEIGHT_PIXELS : SEARCH_IN_BAR_WIDTH_PIXELS,
272
- minWidth: 0,
273
- transition: theme.transitions.create("width", { duration: theme.transitions.duration.short }),
274
- "@media (prefers-reduced-motion: reduce)": { transition: "none" },
275
- [PHONE_MEDIA_QUERY]: { width: isSearchCompact ? BAR_CAPSULE_HEIGHT_PIXELS : "100%" }
276
- },
277
- "& .MuiFormControl-root .MuiInputBase-root.MuiOutlinedInput-root": {
278
- height: BAR_CAPSULE_HEIGHT_PIXELS,
279
- borderRadius: "999px",
280
- cursor: isSearchCompact ? "pointer" : "text"
281
- },
282
- "& .MuiInputAdornment-root": { pointerEvents: "none" },
283
- "& input::placeholder": { opacity: isSearchCompact ? 0 : void 0 }
284
- }),
285
- children: searchBox
316
+ countText: selectionCountText,
317
+ actions: selectionActionItems,
318
+ onClear: clearSelection,
319
+ phoneLayout: selectionPhoneLayout,
320
+ texts: { selection: { clear: t("selection.clear"), actionsLabel: t("selection.actionsLabel"), summaryLabel: t("selection.summaryLabel") } }
286
321
  }
287
322
  ),
288
- header ? /* @__PURE__ */ jsx(Box, { sx: { flexGrow: 1, minWidth: 0 }, children: header({ countText: capped ? t("count.capped", { total: formattedTotal }) : countText, total, capped }) }) : (
323
+ /* @__PURE__ */ jsx(Box, { sx: {
324
+ flexGrow: 1,
325
+ minWidth: 0,
326
+ display: "flex",
327
+ ...hasSelection && { display: "none", ...floatsSelectionOnPhone && { [PHONE_MEDIA_QUERY]: { display: "flex" } } }
328
+ }, children: header ? /* @__PURE__ */ jsx(Box, { sx: { flexGrow: 1, minWidth: 0 }, children: header({ countText: capped ? t("count.capped", { total: formattedTotal }) : countText, total, capped }) }) : (
289
329
  // NOTA: el extra va pegado al conteo y no empujado a la orilla (ml: 'auto'): en escritorio quedaba a todo
290
330
  // el ancho de distancia y no se leía como parte de la misma tabla (caso del selector de año)
291
331
  /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 2, useFlexGap: true, sx: { flexGrow: 1, alignItems: "center", flexWrap: "wrap" }, children: [
@@ -295,7 +335,7 @@ function ServerTable({
295
335
  ] }),
296
336
  toolbarExtra
297
337
  ] })
298
- )
338
+ ) })
299
339
  ] }),
300
340
  beforeQuickFilters,
301
341
  quickFilters.map((quickFilter) => /* @__PURE__ */ jsx(
@@ -321,6 +361,27 @@ function ServerTable({
321
361
  onClearAll: tableState.clearAll
322
362
  }
323
363
  ),
364
+ (offersAllMatching || isSelectable && rowSelection.isAllMatching) && /* @__PURE__ */ jsx(
365
+ Alert,
366
+ {
367
+ severity: "info",
368
+ icon: false,
369
+ sx: { mb: 1, alignItems: "center" },
370
+ action: rowSelection.isAllMatching ? /* @__PURE__ */ jsx(
371
+ Button,
372
+ {
373
+ color: "inherit",
374
+ size: "small",
375
+ onClick: () => updateSelection({
376
+ rowsById: new Map(selectablePageIds.map((id) => [id, pageRowsById.get(id)])),
377
+ isAllMatching: false
378
+ }),
379
+ children: t("selection.onlyThisPage")
380
+ }
381
+ ) : /* @__PURE__ */ jsx(Button, { color: "inherit", size: "small", onClick: () => updateSelection({ rowsById: /* @__PURE__ */ new Map(), isAllMatching: true }), children: capped ? t("selection.selectAllMatchingCapped") : t("selection.selectAllMatching", { total: formattedTotal }) }),
382
+ children: rowSelection.isAllMatching ? capped ? t("selection.allMatchingCapped") : t("selection.allMatching", { total: formattedTotal }) : t("selection.pageSelected", { count: selectablePageIds.length })
383
+ }
384
+ ),
324
385
  error && /* @__PURE__ */ jsx(
325
386
  Alert,
326
387
  {
@@ -334,6 +395,7 @@ function ServerTable({
334
395
  /* @__PURE__ */ jsx(Paper, { variant: "outlined", sx: { width: "100%", minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsx(
335
396
  DataGrid,
336
397
  {
398
+ ...gridProps,
337
399
  rows: visibleRows,
338
400
  columns,
339
401
  columnGroupingModel,
@@ -362,6 +424,13 @@ function ServerTable({
362
424
  hideFooter: isFooterHidden,
363
425
  disableColumnMenu: true,
364
426
  disableRowSelectionOnClick: true,
427
+ checkboxSelection: isSelectable,
428
+ rowSelectionModel: isSelectable ? { type: "include", ids: checkedIds } : void 0,
429
+ onRowSelectionModelChange: isSelectable ? handleGridSelectionChange : void 0,
430
+ isRowSelectable: isSelectable && isRowSelectable ? (params) => isRowSelectable(params.row) : void 0,
431
+ keepNonExistentRowsSelected: isSelectable,
432
+ disableRowSelectionExcludeModel: true,
433
+ hideFooterSelectedRowCount: true,
365
434
  disableVirtualization: Boolean(fixedColumn),
366
435
  density: DATA_GRID_DENSITY,
367
436
  localeText: { ...gridText, noRowsLabel: emptyLabel ?? t("empty.default") },
@@ -395,13 +464,14 @@ function ServerTable({
395
464
  [`& .${STRIPED_ROW_CLASS}`]: { bgcolor: "action.hover" },
396
465
  [`& .${STRIPED_ROW_CLASS}:hover`]: { bgcolor: "action.selected" },
397
466
  ...gridSx
398
- }, (theme) => fixedColumn ? fixedColumnSxOf(theme, fixedColumn) : {}]
467
+ }, (gridTheme) => fixedColumn ? fixedColumnSxOf(gridTheme, fixedColumn, { withCheckboxColumn: isSelectable }) : {}]
399
468
  }
400
469
  ) }),
401
470
  footer?.({ total, capped })
402
- ] });
471
+ ] }) });
403
472
  }
404
473
  export {
474
+ SELECTION_PHONE_LAYOUT,
405
475
  ServerTable as default
406
476
  };
407
477
  //# sourceMappingURL=ServerTable.js.map