@stackcraftmx/calcita 0.28.0 → 0.30.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 (298) hide show
  1. package/README.md +139 -0
  2. package/dist/DataTable/AuditTrailDialog.js +18 -14
  3. package/dist/DataTable/AuditTrailDialog.js.map +1 -1
  4. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +12 -6
  5. package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
  6. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +8 -3
  7. package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
  8. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +26 -18
  9. package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
  10. package/dist/DataTable/EmptyState.js +8 -3
  11. package/dist/DataTable/EmptyState.js.map +1 -1
  12. package/dist/DataTable/FilterChips.js +11 -5
  13. package/dist/DataTable/FilterChips.js.map +1 -1
  14. package/dist/DataTable/GoToPage.js +9 -4
  15. package/dist/DataTable/GoToPage.js.map +1 -1
  16. package/dist/DataTable/ServerTable.js +74 -61
  17. package/dist/DataTable/ServerTable.js.map +1 -1
  18. package/dist/DataTable/columns.js +1 -8
  19. package/dist/DataTable/columns.js.map +1 -1
  20. package/dist/access/PermissionMatrix.js +18 -9
  21. package/dist/access/PermissionMatrix.js.map +1 -1
  22. package/dist/account/AccountSecuritySection.js +8 -3
  23. package/dist/account/AccountSecuritySection.js.map +1 -1
  24. package/dist/account/DemoTotpCodeButton.js +8 -3
  25. package/dist/account/DemoTotpCodeButton.js.map +1 -1
  26. package/dist/account/ProfileScreen.js +19 -13
  27. package/dist/account/ProfileScreen.js.map +1 -1
  28. package/dist/account/SignInScreen.js +6 -2
  29. package/dist/account/SignInScreen.js.map +1 -1
  30. package/dist/appShell/AccountMenu.js +13 -5
  31. package/dist/appShell/AccountMenu.js.map +1 -1
  32. package/dist/appShell/AppLayout.js +100 -71
  33. package/dist/appShell/AppLayout.js.map +1 -1
  34. package/dist/appShell/AppearanceSettings.js +21 -13
  35. package/dist/appShell/AppearanceSettings.js.map +1 -1
  36. package/dist/appShell/AuthLayout.js +26 -13
  37. package/dist/appShell/AuthLayout.js.map +1 -1
  38. package/dist/appShell/BarActions.js +10 -5
  39. package/dist/appShell/BarActions.js.map +1 -1
  40. package/dist/appShell/BarCapsule.js +9 -4
  41. package/dist/appShell/BarCapsule.js.map +1 -1
  42. package/dist/appShell/BarSearch.js +7 -3
  43. package/dist/appShell/BarSearch.js.map +1 -1
  44. package/dist/appShell/HeaderTools.js +20 -15
  45. package/dist/appShell/HeaderTools.js.map +1 -1
  46. package/dist/appShell/MobileTabBar.js +25 -14
  47. package/dist/appShell/MobileTabBar.js.map +1 -1
  48. package/dist/appShell/SettingsMenu.js +7 -3
  49. package/dist/appShell/SettingsMenu.js.map +1 -1
  50. package/dist/appShell/SideMenu.js +53 -35
  51. package/dist/appShell/SideMenu.js.map +1 -1
  52. package/dist/appShell/SideMenuEntry.js +18 -9
  53. package/dist/appShell/SideMenuEntry.js.map +1 -1
  54. package/dist/appShell/SubmenuActionsMenu.js +6 -2
  55. package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
  56. package/dist/appShell/TabBarEditor.js +16 -7
  57. package/dist/appShell/TabBarEditor.js.map +1 -1
  58. package/dist/appShell/TopMenu.js +12 -7
  59. package/dist/appShell/TopMenu.js.map +1 -1
  60. package/dist/appShell/Wordmark.js +6 -2
  61. package/dist/appShell/Wordmark.js.map +1 -1
  62. package/dist/auth/PasswordChecklist.js +14 -7
  63. package/dist/auth/PasswordChecklist.js.map +1 -1
  64. package/dist/calendar/EventCalendar.js +26 -19
  65. package/dist/calendar/EventCalendar.js.map +1 -1
  66. package/dist/calendar/EventDetails.js +28 -13
  67. package/dist/calendar/EventDetails.js.map +1 -1
  68. package/dist/downloads/DownloadManager.js +34 -18
  69. package/dist/downloads/DownloadManager.js.map +1 -1
  70. package/dist/feedback/ConfirmDialog.js +22 -17
  71. package/dist/feedback/ConfirmDialog.js.map +1 -1
  72. package/dist/feedback/ModalWaiting.js +12 -5
  73. package/dist/feedback/ModalWaiting.js.map +1 -1
  74. package/dist/feedback/NewBuildNotice.js +16 -8
  75. package/dist/feedback/NewBuildNotice.js.map +1 -1
  76. package/dist/filepond/FileDropzone.js +39 -31
  77. package/dist/filepond/FileDropzone.js.map +1 -1
  78. package/dist/forms/FormProblemAlert.js +4 -1
  79. package/dist/forms/FormProblemAlert.js.map +1 -1
  80. package/dist/forms/auth/SignInForm.js +6 -2
  81. package/dist/forms/auth/SignInForm.js.map +1 -1
  82. package/dist/forms/security/SecondStepForm.js +6 -2
  83. package/dist/forms/security/SecondStepForm.js.map +1 -1
  84. package/dist/forms/security/TwoStepVerificationCard.js +19 -9
  85. package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
  86. package/dist/hookForm/FormProblemAlert.js +4 -1
  87. package/dist/hookForm/FormProblemAlert.js.map +1 -1
  88. package/dist/inbox/InboxLayout.js +47 -29
  89. package/dist/inbox/InboxLayout.js.map +1 -1
  90. package/dist/inbox/MessageList.js +53 -32
  91. package/dist/inbox/MessageList.js.map +1 -1
  92. package/dist/inbox/MessageView.js +31 -15
  93. package/dist/inbox/MessageView.js.map +1 -1
  94. package/dist/index.js +4 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/inputs/ColorInput.js +22 -17
  97. package/dist/inputs/ColorInput.js.map +1 -1
  98. package/dist/inputs/FormSection.js +64 -64
  99. package/dist/inputs/FormSection.js.map +1 -1
  100. package/dist/inputs/SelectInput.js +21 -16
  101. package/dist/inputs/SelectInput.js.map +1 -1
  102. package/dist/inputs/TextInput.js +25 -20
  103. package/dist/inputs/TextInput.js.map +1 -1
  104. package/dist/inputs/WizardLayout.js +21 -15
  105. package/dist/inputs/WizardLayout.js.map +1 -1
  106. package/dist/kanban/KanbanBoard.js +82 -50
  107. package/dist/kanban/KanbanBoard.js.map +1 -1
  108. package/dist/maps/MapView.js +19 -14
  109. package/dist/maps/MapView.js.map +1 -1
  110. package/dist/pwa/InstallAppSetting.js +4 -1
  111. package/dist/pwa/InstallAppSetting.js.map +1 -1
  112. package/dist/pwa/InstallInstructionsDialog.js +6 -2
  113. package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
  114. package/dist/pwa/InstallPromptBanner.js +10 -5
  115. package/dist/pwa/InstallPromptBanner.js.map +1 -1
  116. package/dist/security/ActivityLog.js +6 -2
  117. package/dist/security/ActivityLog.js.map +1 -1
  118. package/dist/security/RecoveryCodesDialog.js +8 -3
  119. package/dist/security/RecoveryCodesDialog.js.map +1 -1
  120. package/dist/security/SessionsCard.js +12 -5
  121. package/dist/security/SessionsCard.js.map +1 -1
  122. package/dist/theme/components/glassComponents.js +19 -16
  123. package/dist/theme/components/glassComponents.js.map +1 -1
  124. package/dist/theme/components/minimalComponents.js +6 -2
  125. package/dist/theme/components/minimalComponents.js.map +1 -1
  126. package/dist/theme/createToolkitTheme.js +8 -1
  127. package/dist/theme/createToolkitTheme.js.map +1 -1
  128. package/dist/theme/cssTokens.js +23 -0
  129. package/dist/theme/cssTokens.js.map +1 -0
  130. package/dist/theme/glassSurface.js +9 -3
  131. package/dist/theme/glassSurface.js.map +1 -1
  132. package/dist/theme/glassToasterStyles.js +2 -3
  133. package/dist/theme/glassToasterStyles.js.map +1 -1
  134. package/dist/theme/themePieces.js +16 -0
  135. package/dist/theme/themePieces.js.map +1 -0
  136. package/dist/types/DataTable/AuditTrailDialog.d.ts +1 -1
  137. package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +1 -1
  138. package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +1 -1
  139. package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +1 -1
  140. package/dist/types/DataTable/EmptyState.d.ts +1 -1
  141. package/dist/types/DataTable/FilterChips.d.ts +1 -1
  142. package/dist/types/DataTable/GoToPage.d.ts +1 -1
  143. package/dist/types/DataTable/ServerTable.d.ts +1 -1
  144. package/dist/types/access/PermissionMatrix.d.ts +1 -1
  145. package/dist/types/account/AccountSecuritySection.d.ts +1 -1
  146. package/dist/types/account/DemoTotpCodeButton.d.ts +1 -1
  147. package/dist/types/account/ProfileScreen.d.ts +1 -1
  148. package/dist/types/account/SignInScreen.d.ts +1 -1
  149. package/dist/types/appShell/AccountMenu.d.ts +1 -1
  150. package/dist/types/appShell/AppLayout.d.ts +1 -1
  151. package/dist/types/appShell/AppearanceSettings.d.ts +1 -1
  152. package/dist/types/appShell/AuthLayout.d.ts +1 -1
  153. package/dist/types/appShell/BarActions.d.ts +1 -1
  154. package/dist/types/appShell/BarCapsule.d.ts +1 -1
  155. package/dist/types/appShell/BarSearch.d.ts +1 -1
  156. package/dist/types/appShell/HeaderTools.d.ts +1 -1
  157. package/dist/types/appShell/MobileTabBar.d.ts +1 -1
  158. package/dist/types/appShell/SettingsMenu.d.ts +1 -1
  159. package/dist/types/appShell/SideMenu.d.ts +1 -1
  160. package/dist/types/appShell/SideMenuEntry.d.ts +1 -1
  161. package/dist/types/appShell/SubmenuActionsMenu.d.ts +1 -1
  162. package/dist/types/appShell/TabBarEditor.d.ts +1 -1
  163. package/dist/types/appShell/TopMenu.d.ts +1 -1
  164. package/dist/types/appShell/Wordmark.d.ts +1 -1
  165. package/dist/types/auth/PasswordChecklist.d.ts +1 -1
  166. package/dist/types/calendar/EventCalendar.d.ts +1 -1
  167. package/dist/types/calendar/EventDetails.d.ts +1 -1
  168. package/dist/types/feedback/ConfirmDialog.d.ts +1 -1
  169. package/dist/types/feedback/ModalWaiting.d.ts +1 -1
  170. package/dist/types/feedback/NewBuildNotice.d.ts +1 -1
  171. package/dist/types/filepond/FileDropzone.d.ts +1 -1
  172. package/dist/types/forms/auth/SignInForm.d.ts +1 -1
  173. package/dist/types/forms/security/SecondStepForm.d.ts +1 -1
  174. package/dist/types/forms/security/TwoStepVerificationCard.d.ts +1 -1
  175. package/dist/types/inbox/InboxLayout.d.ts +1 -1
  176. package/dist/types/inbox/MessageList.d.ts +1 -1
  177. package/dist/types/inbox/MessageView.d.ts +1 -1
  178. package/dist/types/index.d.ts +1 -0
  179. package/dist/types/inputs/ColorInput.d.ts +1 -1
  180. package/dist/types/inputs/FormSection.d.ts +2 -2
  181. package/dist/types/inputs/SelectInput.d.ts +1 -1
  182. package/dist/types/inputs/TextInput.d.ts +1 -1
  183. package/dist/types/inputs/WizardLayout.d.ts +1 -1
  184. package/dist/types/kanban/KanbanBoard.d.ts +1 -1
  185. package/dist/types/maps/MapView.d.ts +1 -1
  186. package/dist/types/pwa/InstallInstructionsDialog.d.ts +1 -1
  187. package/dist/types/pwa/InstallPromptBanner.d.ts +1 -1
  188. package/dist/types/security/ActivityLog.d.ts +1 -1
  189. package/dist/types/security/RecoveryCodesDialog.d.ts +1 -1
  190. package/dist/types/security/SessionsCard.d.ts +1 -1
  191. package/dist/types/theme/createToolkitTheme.d.ts +12 -1
  192. package/dist/types/theme/cssTokens.d.ts +46 -0
  193. package/dist/types/theme/glassSurface.d.ts +3 -0
  194. package/dist/types/theme/themePieces.d.ts +20 -0
  195. package/dist/types/ui/actions/ActionsMenu.d.ts +1 -1
  196. package/dist/types/ui/actions/DownloadMenu.d.ts +1 -1
  197. package/dist/types/ui/actions/SelectionActions.d.ts +1 -1
  198. package/dist/types/ui/filters/DateRangeFilter.d.ts +1 -1
  199. package/dist/types/ui/filters/PeriodRangeSelector.d.ts +1 -1
  200. package/dist/types/ui/filters/QuickFilters.d.ts +1 -1
  201. package/dist/types/ui/filters/SegmentedControl.d.ts +7 -2
  202. package/dist/types/ui/labels/StatusChip.d.ts +1 -1
  203. package/dist/types/ui/labels/Tag.d.ts +1 -1
  204. package/dist/types/ui/layout/CollapsibleSection.d.ts +1 -1
  205. package/dist/types/ui/layout/ContentDialog.d.ts +1 -1
  206. package/dist/types/ui/layout/DefinitionList.d.ts +1 -1
  207. package/dist/types/ui/layout/DetailHeader.d.ts +1 -1
  208. package/dist/types/ui/layout/ErrorPage.d.ts +1 -1
  209. package/dist/types/ui/layout/GlassPanel.d.ts +1 -1
  210. package/dist/types/ui/layout/PageHeader.d.ts +1 -1
  211. package/dist/types/ui/layout/PageHeading.d.ts +1 -1
  212. package/dist/types/ui/layout/ProfileHeader.d.ts +1 -1
  213. package/dist/types/ui/layout/ScrollableRow.d.ts +1 -1
  214. package/dist/types/ui/layout/SectionCard.d.ts +1 -1
  215. package/dist/types/ui/metrics/BarList.d.ts +1 -1
  216. package/dist/types/ui/metrics/CalendarHeatmap.d.ts +1 -1
  217. package/dist/types/ui/metrics/ChartTooltip.d.ts +1 -1
  218. package/dist/types/ui/metrics/ColumnChart.d.ts +1 -1
  219. package/dist/types/ui/metrics/LineChart.d.ts +1 -1
  220. package/dist/types/ui/metrics/MetricCard.d.ts +1 -1
  221. package/dist/types/ui/metrics/TableToggle.d.ts +1 -1
  222. package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +1 -1
  223. package/dist/types/ui/navigation/FontSizeToggle.d.ts +1 -1
  224. package/dist/types/ui/navigation/LanguageToggle.d.ts +1 -1
  225. package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +1 -1
  226. package/dist/types/ui/timeline/GroupedTimeline.d.ts +1 -1
  227. package/dist/types/upload/FileDropzone.d.ts +1 -1
  228. package/dist/ui/actions/ActionsMenu.js +9 -4
  229. package/dist/ui/actions/ActionsMenu.js.map +1 -1
  230. package/dist/ui/actions/DownloadMenu.js +21 -17
  231. package/dist/ui/actions/DownloadMenu.js.map +1 -1
  232. package/dist/ui/actions/SelectionActions.js +15 -7
  233. package/dist/ui/actions/SelectionActions.js.map +1 -1
  234. package/dist/ui/filters/DateRangeFilter.js +11 -5
  235. package/dist/ui/filters/DateRangeFilter.js.map +1 -1
  236. package/dist/ui/filters/PeriodRangeSelector.js +11 -5
  237. package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
  238. package/dist/ui/filters/QuickFilters.js +16 -8
  239. package/dist/ui/filters/QuickFilters.js.map +1 -1
  240. package/dist/ui/filters/SegmentedControl.js +12 -5
  241. package/dist/ui/filters/SegmentedControl.js.map +1 -1
  242. package/dist/ui/labels/StatusChip.js +35 -25
  243. package/dist/ui/labels/StatusChip.js.map +1 -1
  244. package/dist/ui/labels/Tag.js +8 -3
  245. package/dist/ui/labels/Tag.js.map +1 -1
  246. package/dist/ui/layout/CollapsibleSection.js +18 -8
  247. package/dist/ui/layout/CollapsibleSection.js.map +1 -1
  248. package/dist/ui/layout/ContentDialog.js +13 -6
  249. package/dist/ui/layout/ContentDialog.js.map +1 -1
  250. package/dist/ui/layout/DefinitionList.js +13 -6
  251. package/dist/ui/layout/DefinitionList.js.map +1 -1
  252. package/dist/ui/layout/DetailHeader.js +14 -6
  253. package/dist/ui/layout/DetailHeader.js.map +1 -1
  254. package/dist/ui/layout/ErrorPage.js +20 -15
  255. package/dist/ui/layout/ErrorPage.js.map +1 -1
  256. package/dist/ui/layout/FullPageLoader.js +4 -1
  257. package/dist/ui/layout/FullPageLoader.js.map +1 -1
  258. package/dist/ui/layout/GlassPanel.js +8 -4
  259. package/dist/ui/layout/GlassPanel.js.map +1 -1
  260. package/dist/ui/layout/PageHeader.js +10 -4
  261. package/dist/ui/layout/PageHeader.js.map +1 -1
  262. package/dist/ui/layout/PageHeading.js +17 -8
  263. package/dist/ui/layout/PageHeading.js.map +1 -1
  264. package/dist/ui/layout/ProfileHeader.js +21 -10
  265. package/dist/ui/layout/ProfileHeader.js.map +1 -1
  266. package/dist/ui/layout/ScrollableRow.js +19 -15
  267. package/dist/ui/layout/ScrollableRow.js.map +1 -1
  268. package/dist/ui/layout/SectionCard.js +10 -4
  269. package/dist/ui/layout/SectionCard.js.map +1 -1
  270. package/dist/ui/metrics/BarList.js +9 -4
  271. package/dist/ui/metrics/BarList.js.map +1 -1
  272. package/dist/ui/metrics/CalendarHeatmap.js +40 -21
  273. package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
  274. package/dist/ui/metrics/ChartTooltip.js +6 -2
  275. package/dist/ui/metrics/ChartTooltip.js.map +1 -1
  276. package/dist/ui/metrics/ColumnChart.js +22 -18
  277. package/dist/ui/metrics/ColumnChart.js.map +1 -1
  278. package/dist/ui/metrics/LineChart.js +20 -16
  279. package/dist/ui/metrics/LineChart.js.map +1 -1
  280. package/dist/ui/metrics/MetricCard.js +42 -24
  281. package/dist/ui/metrics/MetricCard.js.map +1 -1
  282. package/dist/ui/metrics/TableToggle.js +7 -2
  283. package/dist/ui/metrics/TableToggle.js.map +1 -1
  284. package/dist/ui/metrics/chartParts.js +14 -5
  285. package/dist/ui/metrics/chartParts.js.map +1 -1
  286. package/dist/ui/navigation/ColorSchemeToggle.js +8 -4
  287. package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
  288. package/dist/ui/navigation/FontSizeToggle.js +6 -2
  289. package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
  290. package/dist/ui/navigation/LanguageToggle.js +8 -4
  291. package/dist/ui/navigation/LanguageToggle.js.map +1 -1
  292. package/dist/ui/navigation/PageBreadcrumbs.js +12 -6
  293. package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
  294. package/dist/ui/timeline/GroupedTimeline.js +26 -13
  295. package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
  296. package/dist/upload/FileDropzone.js +35 -25
  297. package/dist/upload/FileDropzone.js.map +1 -1
  298. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CalendarHeatmap.js","sources":["../../../src/ui/metrics/CalendarHeatmap.jsx"],"sourcesContent":["// Calendario de actividad: un cuadro por día, semanas en columnas (el de GitHub).\n//\n// Sirve para ver RITMO de un vistazo: qué días llegan cargas, si hubo semanas muertas, si la actividad crece. Las\n// reglas de visualización:\n// - es MAGNITUD, así que va un solo tono de claro a oscuro (el del tono que se pida), en cuatro niveles más \"sin\n// actividad\". Nunca un arcoíris. Se arma con color-mix sobre el color del tema (su variable CSS si el tema las usa),\n// sin hexadecimales: en modo oscuro los mismos niveles se ven sobre su propia superficie;\n// - los niveles se cortan por CUARTILES de los días con actividad (heatmapScale.js), no por valores fijos;\n// - leyenda \"Menos → Más\", tooltip por día con el número exacto (el color solo nunca dice cuánto) y VISTA DE TABLA\n// (para lector de pantalla, para quien no distingue el tono y para copiar los números).\n//\n// Sin librería a propósito: el Heatmap de MUI X Charts es de la edición Pro, que pide licencia.\n//\n// Textos por props y del paquete, y fechas con los formatos del paquete.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useMemo, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Stack from '@mui/material/Stack';\nimport Table from '@mui/material/Table';\nimport TableBody from '@mui/material/TableBody';\nimport TableCell from '@mui/material/TableCell';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {HEATMAP_LEVELS, calendarWeeksOf, levelOf} from '@/ui/metrics/heatmapScale.js';\nimport TableToggle, {CHART_VIEW} from '@/ui/metrics/TableToggle.jsx';\n\n// cuadros de 12 px en teléfono (el calendario se desliza) y de 16 px desde md, donde el panel ocupa todo el ancho\nconst CELL_PIXELS = {xs: 12, md: 16};\nconst GAP_PIXELS = 3;\n// solo lunes, miércoles y viernes llevan rótulo: los siete encimados no se leen a 12 px\nconst LABELED_WEEKDAYS = new Set([1, 3, 5]);\n// porcentaje del tono en cada nivel; el nivel 0 es el gris de \"hover\" del tema\nconst LEVEL_TONE_PERCENT = {1: 30, 2: 55, 3: 78, 4: 100};\n\n/**\n * Relleno de un nivel con los colores del tema.\n *\n * @param {object} theme tema de MUI (con o sin variables CSS)\n * @param {string} tone primary, info, success...\n * @param {number} level 0 a 4\n * @returns {string} color CSS\n */\nfunction levelFill(theme, tone, level) {\n // un tema con variables CSS las trae en theme.vars; el tema por omisión de MUI, solo en theme.palette\n const {palette} = theme.vars ?? theme;\n if (level === 0) {\n return palette.action.hover;\n }\n return `color-mix(in srgb, ${palette[tone].main} ${LEVEL_TONE_PERCENT[level]}%, transparent)`;\n}\n\n/**\n * @param {object} props\n * @param {string} props.startsOn primer día del rango (\"2025-09-16\"), fecha de calendario\n * @param {string} props.endsOn último día del rango (\"2026-09-15\")\n * @param {Array<{date: string, value: number, detail?: string}>} props.days solo los días con actividad; el resto vale\n * 0\n * @param {string} props.ariaLabel qué muestra el calendario, ya traducido\n * @param {string} props.valueLabel encabezado de la columna de valor en la tabla (\"Registros leídos\")\n * @param {(value: number) => string} props.describeValue el valor en palabras para el tooltip (\"120 registros leídos\")\n * @param {import('react').ReactNode} [props.summary] renglón de resumen arriba del calendario\n * @param {string} [props.emptyLabel] qué decir si ningún día tiene actividad\n * @param {'primary'|'info'|'success'|'warning'|'error'} [props.tone]\n * @param {'chart'|'table'} [props.defaultView] cómo se ve al abrir (CHART_VIEW); por omisión dibujada\n * @param {(view: 'chart'|'table') => void} [props.onViewChange] la persona cambió a la tabla o a la gráfica\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function CalendarHeatmap({startsOn, endsOn, days, ariaLabel, valueLabel, describeValue, summary, emptyLabel, tone = 'primary', texts, defaultView = CHART_VIEW.chart, onViewChange}) {\n const {t} = useToolkitTranslation('common', texts);\n const {formatDate, formatMonthShortName, formatNumber, formatWeekdayShortName} = useFormatters();\n const [asTable, setAsTable] = useState(defaultView === CHART_VIEW.table);\n const toggleView = () => {\n setAsTable(!asTable);\n onViewChange?.(asTable ? CHART_VIEW.chart : CHART_VIEW.table);\n };\n\n const {weeks, monthMarks, thresholds} = useMemo(() => calendarWeeksOf({startsOn, endsOn, days}), [startsOn, endsOn, days]);\n const activeDays = days.filter((day) => day.value > 0);\n\n return (\n <Box>\n <Stack direction=\"row\" spacing={2} sx={{justifyContent: 'space-between', alignItems: 'center', mb: 1}}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {activeDays.length ? summary : emptyLabel}\n </Typography>\n <Box sx={{flexShrink: 0}}>\n <TableToggle asTable={asTable} onToggle={toggleView} texts={texts} />\n </Box>\n </Stack>\n\n {asTable ? (\n <Box sx={{maxHeight: 260, overflowY: 'auto'}}>\n <Table size=\"small\" aria-label={ariaLabel}>\n <TableHead>\n <TableRow>\n <TableCell>{t('charts.day')}</TableCell>\n <TableCell align=\"right\">{valueLabel}</TableCell>\n <TableCell />\n </TableRow>\n </TableHead>\n <TableBody>\n {[...activeDays].reverse().map((day) => (\n <TableRow key={day.date}>\n <TableCell sx={{whiteSpace: 'nowrap'}}>{formatDate(day.date)}</TableCell>\n <TableCell align=\"right\" sx={{fontVariantNumeric: 'tabular-nums'}}>{formatNumber(day.value)}</TableCell>\n <TableCell sx={{color: 'text.secondary'}}>{day.detail}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </Box>\n ) : (\n // el calendario no se encoge: en pantallas angostas se desliza y arranca mostrando lo más reciente\n <Box sx={{overflowX: 'auto', pb: 0.5}}\n ref={(node) => {\n if (node) {\n node.scrollLeft = node.scrollWidth;\n }\n }}>\n <Box role=\"img\" aria-label={ariaLabel}\n sx={{\n display: 'inline-grid',\n gridTemplateColumns: {\n xs: `auto repeat(${weeks.length}, ${CELL_PIXELS.xs}px)`,\n md: `auto repeat(${weeks.length}, ${CELL_PIXELS.md}px)`,\n },\n columnGap: `${GAP_PIXELS}px`,\n rowGap: `${GAP_PIXELS}px`,\n alignItems: 'center',\n }}>\n <Box />\n {monthMarks.map((month, column) => (\n <Typography key={`month-${column}`} variant=\"caption\" color=\"text.secondary\"\n sx={{fontSize: 10, lineHeight: 1, whiteSpace: 'nowrap', overflow: 'visible', width: CELL_PIXELS}}>\n {month ? formatMonthShortName(month) : ''}\n </Typography>\n ))}\n {[1, 2, 3, 4, 5, 6, 7].map((isoWeekday) => (\n <Box key={`weekday-${isoWeekday}`} sx={{display: 'contents'}}>\n <Typography variant=\"caption\" color=\"text.secondary\"\n sx={{fontSize: 10, lineHeight: {xs: `${CELL_PIXELS.xs}px`, md: `${CELL_PIXELS.md}px`}, pr: 0.75}}>\n {LABELED_WEEKDAYS.has(isoWeekday) ? formatWeekdayShortName(isoWeekday) : ''}\n </Typography>\n {weeks.map((week, column) => {\n const day = week[isoWeekday - 1];\n if (!day) {\n return <Box key={`outside-${column}-${isoWeekday}`} />;\n }\n const level = levelOf(day.value, thresholds);\n return (\n <Tooltip key={day.date} arrow disableInteractive\n title={`${formatDate(day.date)}: ${describeValue(day.value)}${day.detail ? ` · ${day.detail}` : ''}`}>\n <Box sx={(theme) => ({width: CELL_PIXELS, height: CELL_PIXELS, borderRadius: '3px', bgcolor: levelFill(theme, tone, level)})} />\n </Tooltip>\n );\n })}\n </Box>\n ))}\n </Box>\n </Box>\n )}\n\n {!asTable && (\n <Stack direction=\"row\" spacing={0.5} sx={{alignItems: 'center', justifyContent: 'flex-end', mt: 1}} aria-hidden=\"true\">\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{mr: 0.5}}>{t('charts.less')}</Typography>\n {HEATMAP_LEVELS.map((level) => (\n <Box key={level} sx={(theme) => ({width: CELL_PIXELS, height: CELL_PIXELS, borderRadius: '3px', bgcolor: levelFill(theme, tone, level)})} />\n ))}\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ml: 0.5}}>{t('charts.more')}</Typography>\n </Stack>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CalendarHeatmap.js","sources":["../../../src/ui/metrics/CalendarHeatmap.jsx"],"sourcesContent":["// Calendario de actividad: un cuadro por día, semanas en columnas (el de GitHub).\n//\n// Sirve para ver RITMO de un vistazo: qué días llegan cargas, si hubo semanas muertas, si la actividad crece. Las\n// reglas de visualización:\n// - es MAGNITUD, así que va un solo tono de claro a oscuro (el del tono que se pida), en cuatro niveles más \"sin\n// actividad\". Nunca un arcoíris. Se arma con color-mix sobre el color del tema (su variable CSS si el tema las usa),\n// sin hexadecimales: en modo oscuro los mismos niveles se ven sobre su propia superficie;\n// - los niveles se cortan por CUARTILES de los días con actividad (heatmapScale.js), no por valores fijos;\n// - leyenda \"Menos → Más\", tooltip por día con el número exacto (el color solo nunca dice cuánto) y VISTA DE TABLA\n// (para lector de pantalla, para quien no distingue el tono y para copiar los números).\n//\n// Sin librería a propósito: el Heatmap de MUI X Charts es de la edición Pro, que pide licencia.\n//\n// Textos por props y del paquete, y fechas con los formatos del paquete.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaCalendarHeatmap con defaultProps y\n// styleOverrides de sus partes: header, toggle, tableArea, dateCell, valueCell, descriptionCell, gridArea, grid,\n// monthLabel, week, dayLabel, cell, legend, legendLess, legendCell, legendMore.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useMemo, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Stack from '@mui/material/Stack';\nimport Table from '@mui/material/Table';\nimport TableBody from '@mui/material/TableBody';\nimport TableCell from '@mui/material/TableCell';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {HEATMAP_LEVELS, calendarWeeksOf, levelOf} from '@/ui/metrics/heatmapScale.js';\nimport TableToggle, {CHART_VIEW} from '@/ui/metrics/TableToggle.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaCalendarHeatmap)\nconst THEME_NAME = 'CalcitaCalendarHeatmap';\nconst HeatmapHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst HeatmapToggle = piecePart(THEME_NAME, 'Toggle', Box);\nconst HeatmapTableArea = piecePart(THEME_NAME, 'TableArea', Box);\nconst HeatmapDateCell = piecePart(THEME_NAME, 'DateCell', TableCell);\nconst HeatmapValueCell = piecePart(THEME_NAME, 'ValueCell', TableCell);\nconst HeatmapDescriptionCell = piecePart(THEME_NAME, 'DescriptionCell', TableCell);\nconst HeatmapGridArea = piecePart(THEME_NAME, 'GridArea', Box);\nconst HeatmapGrid = piecePart(THEME_NAME, 'Grid', Box);\nconst HeatmapMonthLabel = piecePart(THEME_NAME, 'MonthLabel', Typography);\nconst HeatmapWeek = piecePart(THEME_NAME, 'Week', Box);\nconst HeatmapDayLabel = piecePart(THEME_NAME, 'DayLabel', Typography);\nconst HeatmapCell = piecePart(THEME_NAME, 'Cell', Box);\nconst HeatmapLegend = piecePart(THEME_NAME, 'Legend', Stack);\nconst HeatmapLegendLess = piecePart(THEME_NAME, 'LegendLess', Typography);\nconst HeatmapLegendCell = piecePart(THEME_NAME, 'LegendCell', Box);\nconst HeatmapLegendMore = piecePart(THEME_NAME, 'LegendMore', Typography);\n\n// cuadros de 12 px en teléfono (el calendario se desliza) y de 16 px desde md, donde el panel ocupa todo el ancho\nconst CELL_PIXELS = {xs: 12, md: 16};\nconst GAP_PIXELS = 3;\n// solo lunes, miércoles y viernes llevan rótulo: los siete encimados no se leen a 12 px\nconst LABELED_WEEKDAYS = new Set([1, 3, 5]);\n// porcentaje del tono en cada nivel; el nivel 0 es el gris de \"hover\" del tema\nconst LEVEL_TONE_PERCENT = {1: 30, 2: 55, 3: 78, 4: 100};\n\n/**\n * Relleno de un nivel con los colores del tema.\n *\n * @param {object} theme tema de MUI (con o sin variables CSS)\n * @param {string} tone primary, info, success...\n * @param {number} level 0 a 4\n * @returns {string} color CSS\n */\nfunction levelFill(theme, tone, level) {\n // un tema con variables CSS las trae en theme.vars; el tema por omisión de MUI, solo en theme.palette\n const {palette} = theme.vars ?? theme;\n if (level === 0) {\n return palette.action.hover;\n }\n return `color-mix(in srgb, ${palette[tone].main} ${LEVEL_TONE_PERCENT[level]}%, transparent)`;\n}\n\n/**\n * @param {object} props\n * @param {string} props.startsOn primer día del rango (\"2025-09-16\"), fecha de calendario\n * @param {string} props.endsOn último día del rango (\"2026-09-15\")\n * @param {Array<{date: string, value: number, detail?: string}>} props.days solo los días con actividad; el resto vale\n * 0\n * @param {string} props.ariaLabel qué muestra el calendario, ya traducido\n * @param {string} props.valueLabel encabezado de la columna de valor en la tabla (\"Registros leídos\")\n * @param {(value: number) => string} props.describeValue el valor en palabras para el tooltip (\"120 registros leídos\")\n * @param {import('react').ReactNode} [props.summary] renglón de resumen arriba del calendario\n * @param {string} [props.emptyLabel] qué decir si ningún día tiene actividad\n * @param {'primary'|'info'|'success'|'warning'|'error'} [props.tone]\n * @param {'chart'|'table'} [props.defaultView] cómo se ve al abrir (CHART_VIEW); por omisión dibujada\n * @param {(view: 'chart'|'table') => void} [props.onViewChange] la persona cambió a la tabla o a la gráfica\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function CalendarHeatmap(props) {\n // lo que el proyecto puso en su tema (components.CalcitaCalendarHeatmap.defaultProps) y encima lo que llega aquí\n const {startsOn, endsOn, days, ariaLabel, valueLabel, describeValue, summary, emptyLabel, tone = 'primary', texts, defaultView = CHART_VIEW.chart, onViewChange} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const {formatDate, formatMonthShortName, formatNumber, formatWeekdayShortName} = useFormatters();\n const [asTable, setAsTable] = useState(defaultView === CHART_VIEW.table);\n const toggleView = () => {\n setAsTable(!asTable);\n onViewChange?.(asTable ? CHART_VIEW.chart : CHART_VIEW.table);\n };\n\n const {weeks, monthMarks, thresholds} = useMemo(() => calendarWeeksOf({startsOn, endsOn, days}), [startsOn, endsOn, days]);\n const activeDays = days.filter((day) => day.value > 0);\n\n return (\n <Box>\n <HeatmapHeader direction=\"row\" spacing={2} ownSx={{justifyContent: 'space-between', alignItems: 'center', mb: 1}}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {activeDays.length ? summary : emptyLabel}\n </Typography>\n <HeatmapToggle ownSx={{flexShrink: 0}}>\n <TableToggle asTable={asTable} onToggle={toggleView} texts={texts} />\n </HeatmapToggle>\n </HeatmapHeader>\n\n {asTable ? (\n <HeatmapTableArea ownSx={{maxHeight: 260, overflowY: 'auto'}}>\n <Table size=\"small\" aria-label={ariaLabel}>\n <TableHead>\n <TableRow>\n <TableCell>{t('charts.day')}</TableCell>\n <TableCell align=\"right\">{valueLabel}</TableCell>\n <TableCell />\n </TableRow>\n </TableHead>\n <TableBody>\n {[...activeDays].reverse().map((day) => (\n <TableRow key={day.date}>\n <HeatmapDateCell ownSx={{whiteSpace: 'nowrap'}}>{formatDate(day.date)}</HeatmapDateCell>\n <HeatmapValueCell align=\"right\" ownSx={{fontVariantNumeric: 'tabular-nums'}}>{formatNumber(day.value)}</HeatmapValueCell>\n <HeatmapDescriptionCell ownSx={{color: 'text.secondary'}}>{day.detail}</HeatmapDescriptionCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </HeatmapTableArea>\n ) : (\n // el calendario no se encoge: en pantallas angostas se desliza y arranca mostrando lo más reciente\n <HeatmapGridArea ownSx={{overflowX: 'auto', pb: 0.5}}\n ref={(node) => {\n if (node) {\n node.scrollLeft = node.scrollWidth;\n }\n }}>\n <HeatmapGrid role=\"img\" aria-label={ariaLabel}\n ownSx={{\n display: 'inline-grid',\n gridTemplateColumns: {\n xs: `auto repeat(${weeks.length}, ${CELL_PIXELS.xs}px)`,\n md: `auto repeat(${weeks.length}, ${CELL_PIXELS.md}px)`,\n },\n columnGap: `${GAP_PIXELS}px`,\n rowGap: `${GAP_PIXELS}px`,\n alignItems: 'center',\n }}>\n <Box />\n {monthMarks.map((month, column) => (\n <HeatmapMonthLabel key={`month-${column}`} variant=\"caption\" color=\"text.secondary\"\n ownSx={{fontSize: 10, lineHeight: 1, whiteSpace: 'nowrap', overflow: 'visible', width: CELL_PIXELS}}>\n {month ? formatMonthShortName(month) : ''}\n </HeatmapMonthLabel>\n ))}\n {[1, 2, 3, 4, 5, 6, 7].map((isoWeekday) => (\n <HeatmapWeek key={`weekday-${isoWeekday}`} ownSx={{display: 'contents'}}>\n <HeatmapDayLabel variant=\"caption\" color=\"text.secondary\"\n ownSx={{fontSize: 10, lineHeight: {xs: `${CELL_PIXELS.xs}px`, md: `${CELL_PIXELS.md}px`}, pr: 0.75}}>\n {LABELED_WEEKDAYS.has(isoWeekday) ? formatWeekdayShortName(isoWeekday) : ''}\n </HeatmapDayLabel>\n {weeks.map((week, column) => {\n const day = week[isoWeekday - 1];\n if (!day) {\n return <Box key={`outside-${column}-${isoWeekday}`} />;\n }\n const level = levelOf(day.value, thresholds);\n return (\n <Tooltip key={day.date} arrow disableInteractive\n title={`${formatDate(day.date)}: ${describeValue(day.value)}${day.detail ? ` · ${day.detail}` : ''}`}>\n <HeatmapCell ownSx={(theme) => ({width: CELL_PIXELS, height: CELL_PIXELS, borderRadius: '3px', bgcolor: levelFill(theme, tone, level)})} />\n </Tooltip>\n );\n })}\n </HeatmapWeek>\n ))}\n </HeatmapGrid>\n </HeatmapGridArea>\n )}\n\n {!asTable && (\n <HeatmapLegend direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center', justifyContent: 'flex-end', mt: 1}} aria-hidden=\"true\">\n <HeatmapLegendLess variant=\"caption\" color=\"text.secondary\" ownSx={{mr: 0.5}}>{t('charts.less')}</HeatmapLegendLess>\n {HEATMAP_LEVELS.map((level) => (\n <HeatmapLegendCell key={level} ownSx={(theme) => ({width: CELL_PIXELS, height: CELL_PIXELS, borderRadius: '3px', bgcolor: levelFill(theme, tone, level)})} />\n ))}\n <HeatmapLegendMore variant=\"caption\" color=\"text.secondary\" ownSx={{ml: 0.5}}>{t('charts.more')}</HeatmapLegendMore>\n </HeatmapLegend>\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -3,12 +3,16 @@ import { jsx } from "react/jsx-runtime";
3
3
  import Paper from "@mui/material/Paper";
4
4
  import Typography from "@mui/material/Typography";
5
5
  import { isGlassTheme, glassStylesOf } from "../../theme/glassSurface.js";
6
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
7
+ const THEME_NAME = "CalcitaChartTooltip";
8
+ const TooltipRoot = piecePart(THEME_NAME, "Root", Paper);
6
9
  const CHART_TOOLTIP_OPACITY = 0.92;
7
- function ChartTooltip({ active, payload, render }) {
10
+ function ChartTooltip(props) {
11
+ const { active, payload, render } = usePieceProps(props, THEME_NAME);
8
12
  if (!active || !payload?.length) {
9
13
  return null;
10
14
  }
11
- return /* @__PURE__ */ jsx(Paper, { elevation: 3, sx: [{ px: 1.5, py: 1, pointerEvents: "none" }, (theme) => isGlassTheme(theme) ? glassStylesOf(theme, { opacity: CHART_TOOLTIP_OPACITY }) : {}], children: /* @__PURE__ */ jsx(Typography, { variant: "body2", component: "div", children: render(payload[0].payload) }) });
15
+ return /* @__PURE__ */ jsx(TooltipRoot, { elevation: 3, ownSx: [{ px: 1.5, py: 1, pointerEvents: "none" }, (theme) => isGlassTheme(theme) ? glassStylesOf(theme, { opacity: CHART_TOOLTIP_OPACITY }) : {}], children: /* @__PURE__ */ jsx(Typography, { variant: "body2", component: "div", children: render(payload[0].payload) }) });
12
16
  }
13
17
  export {
14
18
  ChartTooltip as default
@@ -1 +1 @@
1
- {"version":3,"file":"ChartTooltip.js","sources":["../../../src/ui/metrics/ChartTooltip.jsx"],"sourcesContent":["// El tooltip de las gráficas de Recharts con el look de la app. Se exporta para las gráficas propias del proyecto con\n// recharts (una de pastel, una de área): <Tooltip content={<ChartTooltip render={(row) => …} />} />.\n//\n// El de Recharts trae su propio estilo (fondo blanco, borde gris); este usa el Paper del tema y habla igual que los\n// tooltips del resto de la app (y se voltea solo cuando llegue el modo oscuro). Con el vidrio del tema\n// (createToolkitTheme({glass: true})) es de vidrio: flota sobre la gráfica.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\n\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\n\n// el vidrio del cuadrito, casi opaco (el material «grueso»): es para leer una cifra encima de la gráfica; con el vidrio de\n// los controles (60 %) se veía la barra de atrás a través del texto (lo reportaron)\nconst CHART_TOOLTIP_OPACITY = 0.92;\n\n/**\n * @param {{active?: boolean, payload?: Array<{payload: any}>, render: (item: any) => import('react').ReactNode}} props\n * active y payload los pone recharts; render recibe el renglón de data del punto o la columna\n */\nexport default function ChartTooltip({active, payload, render}) {\n // guard clause: Recharts lo monta siempre; solo se pinta sobre una barra\n if (!active || !payload?.length) {\n return null;\n }\n return (\n <Paper elevation={3} sx={[{px: 1.5, py: 1, pointerEvents: 'none'}, (theme) => (isGlassTheme(theme) ? glassStylesOf(theme, {opacity: CHART_TOOLTIP_OPACITY}) : {})]}>\n {/* div: el globo puede llevar varios renglones (una serie por renglón) */}\n <Typography variant=\"body2\" component=\"div\">{render(payload[0].payload)}</Typography>\n </Paper>\n );\n}\n"],"names":[],"mappings":";;;;;AAeA,MAAM,wBAAwB;AAM9B,SAAwB,aAAa,EAAC,QAAQ,SAAS,UAAS;AAE5D,MAAI,CAAC,UAAU,CAAC,SAAS,QAAQ;AAC7B,WAAO;AAAA,EACX;AACA,6BACK,OAAA,EAAM,WAAW,GAAG,IAAI,CAAC,EAAC,IAAI,KAAK,IAAI,GAAG,eAAe,UAAS,CAAC,UAAW,aAAa,KAAK,IAAI,cAAc,OAAO,EAAC,SAAS,sBAAA,CAAsB,IAAI,EAAG,GAE7J,UAAA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,WAAU,OAAO,UAAA,OAAO,QAAQ,CAAC,EAAE,OAAO,GAAE,GAC5E;AAER;"}
1
+ {"version":3,"file":"ChartTooltip.js","sources":["../../../src/ui/metrics/ChartTooltip.jsx"],"sourcesContent":["// El tooltip de las gráficas de Recharts con el look de la app. Se exporta para las gráficas propias del proyecto con\n// recharts (una de pastel, una de área): <Tooltip content={<ChartTooltip render={(row) => …} />} />.\n//\n// El de Recharts trae su propio estilo (fondo blanco, borde gris); este usa el Paper del tema y habla igual que los\n// tooltips del resto de la app (y se voltea solo cuando llegue el modo oscuro). Con el vidrio del tema\n// (createToolkitTheme({glass: true})) es de vidrio: flota sobre la gráfica.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaChartTooltip con defaultProps y\n// styleOverrides de sus partes: root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\n\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaChartTooltip)\nconst THEME_NAME = 'CalcitaChartTooltip';\nconst TooltipRoot = piecePart(THEME_NAME, 'Root', Paper);\n\n// el vidrio del cuadrito, casi opaco (el material «grueso»): es para leer una cifra encima de la gráfica; con el vidrio de\n// los controles (60 %) se veía la barra de atrás a través del texto (lo reportaron)\nconst CHART_TOOLTIP_OPACITY = 0.92;\n\n/**\n * @param {{active?: boolean, payload?: Array<{payload: any}>, render: (item: any) => import('react').ReactNode}} props\n * active y payload los pone recharts; render recibe el renglón de data del punto o la columna\n */\nexport default function ChartTooltip(props) {\n // lo que el proyecto puso en su tema (components.CalcitaChartTooltip.defaultProps) y encima lo que llega aquí\n const {active, payload, render} = usePieceProps(props, THEME_NAME);\n // guard clause: Recharts lo monta siempre; solo se pinta sobre una barra\n if (!active || !payload?.length) {\n return null;\n }\n return (\n <TooltipRoot elevation={3} ownSx={[{px: 1.5, py: 1, pointerEvents: 'none'}, (theme) => (isGlassTheme(theme) ? glassStylesOf(theme, {opacity: CHART_TOOLTIP_OPACITY}) : {})]}>\n {/* div: el globo puede llevar varios renglones (una serie por renglón) */}\n <Typography variant=\"body2\" component=\"div\">{render(payload[0].payload)}</Typography>\n </TooltipRoot>\n );\n}\n"],"names":[],"mappings":";;;;;;AAkBA,MAAM,aAAa;AACnB,MAAM,cAAc,UAAU,YAAY,QAAQ,KAAK;AAIvD,MAAM,wBAAwB;AAM9B,SAAwB,aAAa,OAAO;AAExC,QAAM,EAAC,QAAQ,SAAS,WAAU,cAAc,OAAO,UAAU;AAEjE,MAAI,CAAC,UAAU,CAAC,SAAS,QAAQ;AAC7B,WAAO;AAAA,EACX;AACA,6BACK,aAAA,EAAY,WAAW,GAAG,OAAO,CAAC,EAAC,IAAI,KAAK,IAAI,GAAG,eAAe,UAAS,CAAC,UAAW,aAAa,KAAK,IAAI,cAAc,OAAO,EAAC,SAAS,sBAAA,CAAsB,IAAI,EAAG,GAEtK,UAAA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,WAAU,OAAO,UAAA,OAAO,QAAQ,CAAC,EAAE,OAAO,GAAE,GAC5E;AAER;"}
@@ -9,24 +9,28 @@ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
9
9
  import ChartTooltip from "./ChartTooltip.js";
10
10
  import TableToggle, { CHART_VIEW } from "./TableToggle.js";
11
11
  import { useChartColors, resolvedSeriesOf, highestValueOf, ChartTable, valueAxisWidthOf, ChartLegend, ReferenceCaption } from "./chartParts.js";
12
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
13
+ const THEME_NAME = "CalcitaColumnChart";
14
+ const ColumnChartPart = piecePart(THEME_NAME, "Chart", Box);
12
15
  const SINGLE_SERIES_KEY = "value";
13
- function ColumnChart({
14
- data,
15
- periodLabel,
16
- valueLabel,
17
- series,
18
- ariaLabel,
19
- emptyLabel,
20
- height = 220,
21
- tone = "primary",
22
- onSelect,
23
- formatValue: formatValueProp,
24
- referenceLine,
25
- renderTooltip,
26
- texts,
27
- defaultView = CHART_VIEW.chart,
28
- onViewChange
29
- }) {
16
+ function ColumnChart(props) {
17
+ const {
18
+ data,
19
+ periodLabel,
20
+ valueLabel,
21
+ series,
22
+ ariaLabel,
23
+ emptyLabel,
24
+ height = 220,
25
+ tone = "primary",
26
+ onSelect,
27
+ formatValue: formatValueProp,
28
+ referenceLine,
29
+ renderTooltip,
30
+ texts,
31
+ defaultView = CHART_VIEW.chart,
32
+ onViewChange
33
+ } = usePieceProps(props, THEME_NAME);
30
34
  const { t } = useToolkitTranslation("common", texts);
31
35
  const { formatNumber } = useFormatters();
32
36
  const formatValue = formatValueProp ?? formatNumber;
@@ -52,7 +56,7 @@ function ColumnChart({
52
56
  ] }));
53
57
  return /* @__PURE__ */ jsxs(Box, { children: [
54
58
  /* @__PURE__ */ jsx(TableToggle, { asTable, onToggle: toggleView, texts }),
55
- asTable ? /* @__PURE__ */ jsx(ChartTable, { data, series: drawnSeries, periodLabel, formatValue, height }) : /* @__PURE__ */ jsx(Box, { role: "img", "aria-label": ariaLabel, sx: { height, color: `${tone}.main` }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data, margin: { top: 8, right: 8, bottom: 0, left: -8 }, barCategoryGap: 4, children: [
59
+ asTable ? /* @__PURE__ */ jsx(ChartTable, { data, series: drawnSeries, periodLabel, formatValue, height }) : /* @__PURE__ */ jsx(ColumnChartPart, { role: "img", "aria-label": ariaLabel, ownSx: { height, color: `${tone}.main` }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data, margin: { top: 8, right: 8, bottom: 0, left: -8 }, barCategoryGap: 4, children: [
56
60
  /* @__PURE__ */ jsx(CartesianGrid, { vertical: false, stroke: colors.grid }),
57
61
  /* @__PURE__ */ jsx(
58
62
  XAxis,
@@ -1 +1 @@
1
- {"version":3,"file":"ColumnChart.js","sources":["../../../src/ui/metrics/ColumnChart.jsx"],"sourcesContent":["// Columnas a lo largo del tiempo (p. ej. registros leídos por mes), dibujadas con Recharts: una serie (data con `value`) o\n// varias agrupadas por periodo (`series`, cada una con su llave en data: ventas y devoluciones por mes).\n//\n// Recharts y no SVG a mano: ejes, escalas, rótulos que no se encimen y el tooltip ya los resolvió la librería. Lo que se\n// decide aquí son las reglas de visualización:\n// - una serie → un color (el del tono vía currentColor) y SIN leyenda: el título ya dice qué es; varias → cada una su color\n// y leyenda abajo (chartParts.jsx, lo que comparte con LineChart);\n// - columnas de 28 px como máximo, punta redondeada, base cuadrada;\n// - rejilla horizontal de 1 px, sólida y recesiva; el texto de los ejes en tinta secundaria;\n// - tooltip por columna y VISTA DE TABLA;\n// - una línea de referencia opcional (p. ej. la meta), punteada y en tinta secundaria: se lee sin competir con las\n// columnas, y el eje crece lo necesario para que siempre se vea. Su rótulo va debajo de la gráfica y no sobre la\n// línea, donde se encimaba con la columna del último mes; así también lo lee quien ve la tabla.\n//\n// El valor se escribe con formatValue (por omisión, número con separador de miles): con él una serie puede ir en\n// porcentaje. El eje mide lo que mide su número más largo, para que «2,500,000» no salga cortado.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport {Bar, BarChart, CartesianGrid, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ChartTooltip from '@/ui/metrics/ChartTooltip.jsx';\nimport TableToggle, {CHART_VIEW} from '@/ui/metrics/TableToggle.jsx';\nimport {\n ChartLegend, ChartTable, ReferenceCaption, highestValueOf, resolvedSeriesOf, useChartColors, valueAxisWidthOf,\n} from '@/ui/metrics/chartParts.jsx';\n\n// la llave del valor cuando la gráfica es de una sola serie\nconst SINGLE_SERIES_KEY = 'value';\n\n/**\n * @param {object} props\n * @param {Array<Record<string, any>>} props.data un renglón por periodo: {key, label, value, tooltip?} con una serie, o\n * {key, label, [llave de cada serie]: número} con varias\n * @param {string} props.periodLabel encabezado de la primera columna de la tabla («Mes»), ya traducido\n * @param {string} [props.valueLabel] cómo se llama lo que se cuenta, ya traducido (una serie)\n * @param {Array<{key: string, label: string, tone?: 'primary'|'secondary'|'success'|'warning'|'error'|'info'}>} [props.series]\n * varias series agrupadas: la llave de cada una en data, su nombre ya traducido y su color (por omisión, en orden)\n * @param {string} props.ariaLabel\n * @param {string} props.emptyLabel\n * @param {number} [props.height]\n * @param {'primary'|'success'|'warning'|'error'|'info'} [props.tone] el color de una sola serie\n * @param {(item: any, seriesKey: string) => void} [props.onSelect] recibe el renglón de data que se picó y la serie\n * @param {(value: number) => string} [props.formatValue] cómo se escribe un valor (en el eje, en la tabla y en el\n * tooltip)\n * @param {{value: number, label: string}} [props.referenceLine] línea de referencia con su rótulo ya traducido («Meta:\n * 10 %»)\n * @param {(row: any) => import('react').ReactNode} [props.renderTooltip] lo que dice el globo de un periodo; por omisión su\n * nombre y el valor de cada serie\n * @param {'chart'|'table'} [props.defaultView] cómo se ve al abrir (CHART_VIEW); por omisión dibujada\n * @param {(view: 'chart'|'table') => void} [props.onViewChange] la persona cambió a la tabla o a la gráfica\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {charts: {viewAsTable, viewAsChart, legend}}\n */\nexport default function ColumnChart({\n data, periodLabel, valueLabel, series, ariaLabel, emptyLabel, height = 220, tone = 'primary', onSelect, formatValue: formatValueProp,\n referenceLine, renderTooltip, texts, defaultView = CHART_VIEW.chart, onViewChange,\n}) {\n const {t} = useToolkitTranslation('common', texts);\n const {formatNumber} = useFormatters();\n const formatValue = formatValueProp ?? formatNumber;\n const [asTable, setAsTable] = useState(defaultView === CHART_VIEW.table);\n const toggleView = () => {\n setAsTable(!asTable);\n onViewChange?.(asTable ? CHART_VIEW.chart : CHART_VIEW.table);\n };\n const isSingleSeries = !series?.length;\n const colors = useChartColors();\n const drawnSeries = resolvedSeriesOf(isSingleSeries ? [{key: SINGLE_SERIES_KEY, label: valueLabel, tone}] : series, colors.palette);\n\n // guard clause: sin datos se dice, no se pinta un eje vacío\n if (!data.length) {\n return <Typography variant=\"body2\" color=\"text.secondary\">{emptyLabel}</Typography>;\n }\n\n // el valor más alto que se dibuja (el de las columnas o el de la línea de referencia) decide el ancho del eje\n const highestValue = highestValueOf(data, drawnSeries, referenceLine);\n const tooltipOf = renderTooltip ?? ((row) => (isSingleSeries\n ? row.tooltip ?? `${row.label}: ${formatValue(row.value)}`\n : (\n <>\n <strong>{row.label}</strong>\n {drawnSeries.map((item) => <Box key={item.key}>{item.label}: {formatValue(row[item.key])}</Box>)}\n </>\n )));\n\n return (\n <Box>\n <TableToggle asTable={asTable} onToggle={toggleView} texts={texts} />\n {asTable ? (\n <ChartTable data={data} series={drawnSeries} periodLabel={periodLabel} formatValue={formatValue} height={height} />\n ) : (\n <Box role=\"img\" aria-label={ariaLabel} sx={{height, color: `${tone}.main`}}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={data} margin={{top: 8, right: 8, bottom: 0, left: -8}} barCategoryGap={4}>\n <CartesianGrid vertical={false} stroke={colors.grid} />\n <XAxis dataKey=\"label\" tick={colors.axisText} tickLine={false} axisLine={{stroke: colors.grid}}\n interval=\"preserveStartEnd\" minTickGap={12} />\n <YAxis allowDecimals={false} tick={colors.axisText} tickLine={false} axisLine={false}\n width={valueAxisWidthOf(highestValue, formatValue)} tickFormatter={(value) => formatValue(value)} />\n <Tooltip cursor={{fill: colors.hover}} content={<ChartTooltip render={tooltipOf} />} />\n {referenceLine && (\n // extendDomain: si la referencia queda arriba de todas las columnas, el eje crece hasta ella\n <ReferenceLine y={referenceLine.value} ifOverflow=\"extendDomain\" stroke={colors.axis} strokeDasharray=\"4 4\" />\n )}\n {drawnSeries.map((item) => (\n // una serie: currentColor (el tono de la caja); varias: el color de cada una\n <Bar key={item.key} dataKey={item.key} fill={isSingleSeries ? 'currentColor' : item.color} maxBarSize={28}\n radius={[4, 4, 0, 0]} isAnimationActive={false} cursor={onSelect ? 'pointer' : 'default'}\n onClick={onSelect ? (entry) => onSelect(entry.payload, item.key) : undefined} />\n ))}\n </BarChart>\n </ResponsiveContainer>\n </Box>\n )}\n <ChartLegend series={drawnSeries} ariaLabel={t('charts.legend')} />\n <ReferenceCaption referenceLine={referenceLine} />\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ColumnChart.js","sources":["../../../src/ui/metrics/ColumnChart.jsx"],"sourcesContent":["// Columnas a lo largo del tiempo (p. ej. registros leídos por mes), dibujadas con Recharts: una serie (data con `value`) o\n// varias agrupadas por periodo (`series`, cada una con su llave en data: ventas y devoluciones por mes).\n//\n// Recharts y no SVG a mano: ejes, escalas, rótulos que no se encimen y el tooltip ya los resolvió la librería. Lo que se\n// decide aquí son las reglas de visualización:\n// - una serie → un color (el del tono vía currentColor) y SIN leyenda: el título ya dice qué es; varias → cada una su color\n// y leyenda abajo (chartParts.jsx, lo que comparte con LineChart);\n// - columnas de 28 px como máximo, punta redondeada, base cuadrada;\n// - rejilla horizontal de 1 px, sólida y recesiva; el texto de los ejes en tinta secundaria;\n// - tooltip por columna y VISTA DE TABLA;\n// - una línea de referencia opcional (p. ej. la meta), punteada y en tinta secundaria: se lee sin competir con las\n// columnas, y el eje crece lo necesario para que siempre se vea. Su rótulo va debajo de la gráfica y no sobre la\n// línea, donde se encimaba con la columna del último mes; así también lo lee quien ve la tabla.\n//\n// El valor se escribe con formatValue (por omisión, número con separador de miles): con él una serie puede ir en\n// porcentaje. El eje mide lo que mide su número más largo, para que «2,500,000» no salga cortado.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaColumnChart con defaultProps y\n// styleOverrides de sus partes: chart.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport {Bar, BarChart, CartesianGrid, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ChartTooltip from '@/ui/metrics/ChartTooltip.jsx';\nimport TableToggle, {CHART_VIEW} from '@/ui/metrics/TableToggle.jsx';\nimport {\n ChartLegend, ChartTable, ReferenceCaption, highestValueOf, resolvedSeriesOf, useChartColors, valueAxisWidthOf,\n} from '@/ui/metrics/chartParts.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaColumnChart)\nconst THEME_NAME = 'CalcitaColumnChart';\nconst ColumnChartPart = piecePart(THEME_NAME, 'Chart', Box);\n\n// la llave del valor cuando la gráfica es de una sola serie\nconst SINGLE_SERIES_KEY = 'value';\n\n/**\n * @param {object} props\n * @param {Array<Record<string, any>>} props.data un renglón por periodo: {key, label, value, tooltip?} con una serie, o\n * {key, label, [llave de cada serie]: número} con varias\n * @param {string} props.periodLabel encabezado de la primera columna de la tabla («Mes»), ya traducido\n * @param {string} [props.valueLabel] cómo se llama lo que se cuenta, ya traducido (una serie)\n * @param {Array<{key: string, label: string, tone?: 'primary'|'secondary'|'success'|'warning'|'error'|'info'}>} [props.series]\n * varias series agrupadas: la llave de cada una en data, su nombre ya traducido y su color (por omisión, en orden)\n * @param {string} props.ariaLabel\n * @param {string} props.emptyLabel\n * @param {number} [props.height]\n * @param {'primary'|'success'|'warning'|'error'|'info'} [props.tone] el color de una sola serie\n * @param {(item: any, seriesKey: string) => void} [props.onSelect] recibe el renglón de data que se picó y la serie\n * @param {(value: number) => string} [props.formatValue] cómo se escribe un valor (en el eje, en la tabla y en el\n * tooltip)\n * @param {{value: number, label: string}} [props.referenceLine] línea de referencia con su rótulo ya traducido («Meta:\n * 10 %»)\n * @param {(row: any) => import('react').ReactNode} [props.renderTooltip] lo que dice el globo de un periodo; por omisión su\n * nombre y el valor de cada serie\n * @param {'chart'|'table'} [props.defaultView] cómo se ve al abrir (CHART_VIEW); por omisión dibujada\n * @param {(view: 'chart'|'table') => void} [props.onViewChange] la persona cambió a la tabla o a la gráfica\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {charts: {viewAsTable, viewAsChart, legend}}\n */\nexport default function ColumnChart(props) {\n // lo que el proyecto puso en su tema (components.CalcitaColumnChart.defaultProps) y encima lo que llega aquí\n const {\n data, periodLabel, valueLabel, series, ariaLabel, emptyLabel, height = 220, tone = 'primary', onSelect, formatValue: formatValueProp,\n referenceLine, renderTooltip, texts, defaultView = CHART_VIEW.chart, onViewChange,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const {formatNumber} = useFormatters();\n const formatValue = formatValueProp ?? formatNumber;\n const [asTable, setAsTable] = useState(defaultView === CHART_VIEW.table);\n const toggleView = () => {\n setAsTable(!asTable);\n onViewChange?.(asTable ? CHART_VIEW.chart : CHART_VIEW.table);\n };\n const isSingleSeries = !series?.length;\n const colors = useChartColors();\n const drawnSeries = resolvedSeriesOf(isSingleSeries ? [{key: SINGLE_SERIES_KEY, label: valueLabel, tone}] : series, colors.palette);\n\n // guard clause: sin datos se dice, no se pinta un eje vacío\n if (!data.length) {\n return <Typography variant=\"body2\" color=\"text.secondary\">{emptyLabel}</Typography>;\n }\n\n // el valor más alto que se dibuja (el de las columnas o el de la línea de referencia) decide el ancho del eje\n const highestValue = highestValueOf(data, drawnSeries, referenceLine);\n const tooltipOf = renderTooltip ?? ((row) => (isSingleSeries\n ? row.tooltip ?? `${row.label}: ${formatValue(row.value)}`\n : (\n <>\n <strong>{row.label}</strong>\n {drawnSeries.map((item) => <Box key={item.key}>{item.label}: {formatValue(row[item.key])}</Box>)}\n </>\n )));\n\n return (\n <Box>\n <TableToggle asTable={asTable} onToggle={toggleView} texts={texts} />\n {asTable ? (\n <ChartTable data={data} series={drawnSeries} periodLabel={periodLabel} formatValue={formatValue} height={height} />\n ) : (\n <ColumnChartPart role=\"img\" aria-label={ariaLabel} ownSx={{height, color: `${tone}.main`}}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={data} margin={{top: 8, right: 8, bottom: 0, left: -8}} barCategoryGap={4}>\n <CartesianGrid vertical={false} stroke={colors.grid} />\n <XAxis dataKey=\"label\" tick={colors.axisText} tickLine={false} axisLine={{stroke: colors.grid}}\n interval=\"preserveStartEnd\" minTickGap={12} />\n <YAxis allowDecimals={false} tick={colors.axisText} tickLine={false} axisLine={false}\n width={valueAxisWidthOf(highestValue, formatValue)} tickFormatter={(value) => formatValue(value)} />\n <Tooltip cursor={{fill: colors.hover}} content={<ChartTooltip render={tooltipOf} />} />\n {referenceLine && (\n // extendDomain: si la referencia queda arriba de todas las columnas, el eje crece hasta ella\n <ReferenceLine y={referenceLine.value} ifOverflow=\"extendDomain\" stroke={colors.axis} strokeDasharray=\"4 4\" />\n )}\n {drawnSeries.map((item) => (\n // una serie: currentColor (el tono de la caja); varias: el color de cada una\n <Bar key={item.key} dataKey={item.key} fill={isSingleSeries ? 'currentColor' : item.color} maxBarSize={28}\n radius={[4, 4, 0, 0]} isAnimationActive={false} cursor={onSelect ? 'pointer' : 'default'}\n onClick={onSelect ? (entry) => onSelect(entry.payload, item.key) : undefined} />\n ))}\n </BarChart>\n </ResponsiveContainer>\n </ColumnChartPart>\n )}\n <ChartLegend series={drawnSeries} ariaLabel={t('charts.legend')} />\n <ReferenceCaption referenceLine={referenceLine} />\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -9,24 +9,28 @@ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
9
9
  import ChartTooltip from "./ChartTooltip.js";
10
10
  import TableToggle, { CHART_VIEW } from "./TableToggle.js";
11
11
  import { useChartColors, resolvedSeriesOf, highestValueOf, ChartTable, valueAxisWidthOf, ChartLegend, ReferenceCaption } from "./chartParts.js";
12
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
13
+ const THEME_NAME = "CalcitaLineChart";
14
+ const LineChartPart = piecePart(THEME_NAME, "Chart", Box);
12
15
  const LINE_WIDTH_PIXELS = 2;
13
16
  const DOT_RADIUS_PIXELS = 3;
14
17
  const ACTIVE_DOT_RADIUS_PIXELS = 5;
15
- function LineChart({
16
- data,
17
- series,
18
- periodLabel,
19
- ariaLabel,
20
- emptyLabel,
21
- height = 220,
22
- formatValue: formatValueProp,
23
- referenceLine,
24
- onSelect,
25
- renderTooltip,
26
- texts,
27
- defaultView = CHART_VIEW.chart,
28
- onViewChange
29
- }) {
18
+ function LineChart(props) {
19
+ const {
20
+ data,
21
+ series,
22
+ periodLabel,
23
+ ariaLabel,
24
+ emptyLabel,
25
+ height = 220,
26
+ formatValue: formatValueProp,
27
+ referenceLine,
28
+ onSelect,
29
+ renderTooltip,
30
+ texts,
31
+ defaultView = CHART_VIEW.chart,
32
+ onViewChange
33
+ } = usePieceProps(props, THEME_NAME);
30
34
  const { t } = useToolkitTranslation("common", texts);
31
35
  const { formatNumber } = useFormatters();
32
36
  const formatValue = formatValueProp ?? formatNumber;
@@ -57,7 +61,7 @@ function LineChart({
57
61
  ] }));
58
62
  return /* @__PURE__ */ jsxs(Box, { children: [
59
63
  /* @__PURE__ */ jsx(TableToggle, { asTable, onToggle: toggleView, texts }),
60
- asTable ? /* @__PURE__ */ jsx(ChartTable, { data, series: drawnSeries, periodLabel, formatValue, height }) : /* @__PURE__ */ jsx(Box, { role: "img", "aria-label": ariaLabel, sx: { height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
64
+ asTable ? /* @__PURE__ */ jsx(ChartTable, { data, series: drawnSeries, periodLabel, formatValue, height }) : /* @__PURE__ */ jsx(LineChartPart, { role: "img", "aria-label": ariaLabel, ownSx: { height }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
61
65
  LineChart$1,
62
66
  {
63
67
  data,
@@ -1 +1 @@
1
- {"version":3,"file":"LineChart.js","sources":["../../../src/ui/metrics/LineChart.jsx"],"sourcesContent":["// Líneas a lo largo del tiempo, de una serie o de varias que se comparan (ventas contra devoluciones, este año contra el\n// pasado), dibujadas con Recharts. Lo que comparte con ColumnChart (ejes, tabla, leyenda, línea de referencia) vive en\n// chartParts.jsx.\n//\n// Las reglas de la guía de Apple (HIG «Charts» y «Charting data»):\n// - líneas para ver la tendencia; columnas (ColumnChart) para comparar periodos sueltos;\n// - cada serie con su color Y su trazo (continuo, rayado, punteado), con su punto en cada periodo: el color no va solo;\n// - una serie sin leyenda (el título ya dice qué es); varias, con leyenda abajo; toda gráfica con su vista de tabla;\n// - el globo de un periodo dice el valor de cada serie.\n//\n// <LineChart data={[{key: '2026-4', label: 'abr', sales: 120, returns: 8}, …]} periodLabel=\"Mes\"\n// series={[{key: 'sales', label: 'Ventas'}, {key: 'returns', label: 'Devoluciones', tone: 'error'}]}\n// ariaLabel=\"Ventas y devoluciones por mes\" emptyLabel=\"Sin datos\" />\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport {CartesianGrid, Line, LineChart as RechartsLineChart, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ChartTooltip from '@/ui/metrics/ChartTooltip.jsx';\nimport TableToggle, {CHART_VIEW} from '@/ui/metrics/TableToggle.jsx';\nimport {\n ChartLegend, ChartTable, ReferenceCaption, highestValueOf, resolvedSeriesOf, useChartColors, valueAxisWidthOf,\n} from '@/ui/metrics/chartParts.jsx';\n\n// el grueso de la línea y el tamaño de su punto (px): se ven en el teléfono sin tapar a la vecina\nconst LINE_WIDTH_PIXELS = 2;\nconst DOT_RADIUS_PIXELS = 3;\nconst ACTIVE_DOT_RADIUS_PIXELS = 5;\n\n/**\n * @param {object} props\n * @param {Array<Record<string, any>>} props.data un renglón por periodo: {key, label, [llave de cada serie]: número}\n * @param {Array<{key: string, label: string, tone?: 'primary'|'secondary'|'success'|'warning'|'error'|'info', dash?: string}>} props.series\n * la llave de cada serie en data, su nombre ya traducido, su color y su trazo (por omisión, en orden)\n * @param {string} props.periodLabel encabezado de la primera columna de la tabla («Mes»), ya traducido\n * @param {string} props.ariaLabel\n * @param {string} props.emptyLabel\n * @param {number} [props.height]\n * @param {(value: number) => string} [props.formatValue] cómo se escribe un valor (en el eje, en la tabla y en el globo)\n * @param {{value: number, label: string}} [props.referenceLine] línea de referencia (la meta) con su rótulo ya traducido\n * @param {(row: any) => void} [props.onSelect] recibe el renglón de data del periodo que se picó\n * @param {(row: any) => import('react').ReactNode} [props.renderTooltip] lo que dice el globo de un periodo; por omisión su\n * nombre y el valor de cada serie\n * @param {'chart'|'table'} [props.defaultView] cómo se ve al abrir (CHART_VIEW); por omisión dibujada\n * @param {(view: 'chart'|'table') => void} [props.onViewChange] la persona cambió a la tabla o a la gráfica\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {charts: {viewAsTable, viewAsChart, legend}}\n */\nexport default function LineChart({\n data, series, periodLabel, ariaLabel, emptyLabel, height = 220, formatValue: formatValueProp, referenceLine, onSelect, renderTooltip, texts, defaultView = CHART_VIEW.chart, onViewChange,\n}) {\n const {t} = useToolkitTranslation('common', texts);\n const {formatNumber} = useFormatters();\n const formatValue = formatValueProp ?? formatNumber;\n const [asTable, setAsTable] = useState(defaultView === CHART_VIEW.table);\n const toggleView = () => {\n setAsTable(!asTable);\n onViewChange?.(asTable ? CHART_VIEW.chart : CHART_VIEW.table);\n };\n const colors = useChartColors();\n const drawnSeries = resolvedSeriesOf(series, colors.palette);\n\n // guard clause: sin datos se dice, no se pinta un eje vacío\n if (!data.length) {\n return <Typography variant=\"body2\" color=\"text.secondary\">{emptyLabel}</Typography>;\n }\n\n const highestValue = highestValueOf(data, drawnSeries, referenceLine);\n // el periodo que se picó: recharts da su posición en data (a veces como texto)\n const selectPeriodAt = (activeIndex) => {\n const row = data[Number(activeIndex)];\n if (row) {\n onSelect(row);\n }\n };\n const tooltipOf = renderTooltip ?? ((row) => (\n <>\n <strong>{row.label}</strong>\n {drawnSeries.map((item) => <Box key={item.key}>{item.label}: {formatValue(row[item.key])}</Box>)}\n </>\n ));\n\n return (\n <Box>\n <TableToggle asTable={asTable} onToggle={toggleView} texts={texts} />\n {asTable ? (\n <ChartTable data={data} series={drawnSeries} periodLabel={periodLabel} formatValue={formatValue} height={height} />\n ) : (\n <Box role=\"img\" aria-label={ariaLabel} sx={{height}}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {/* OJO: recharts 3 avisa el clic con el índice del periodo (activeIndex), ya no con activePayload (el de\n la versión 2) */}\n <RechartsLineChart data={data} margin={{top: 8, right: 12, bottom: 0, left: -8}}\n onClick={onSelect ? (state) => selectPeriodAt(state?.activeIndex) : undefined}>\n <CartesianGrid vertical={false} stroke={colors.grid} />\n <XAxis dataKey=\"label\" tick={colors.axisText} tickLine={false} axisLine={{stroke: colors.grid}}\n interval=\"preserveStartEnd\" minTickGap={12} />\n <YAxis allowDecimals={false} tick={colors.axisText} tickLine={false} axisLine={false}\n width={valueAxisWidthOf(highestValue, formatValue)} tickFormatter={(value) => formatValue(value)} />\n <Tooltip cursor={{stroke: colors.grid}} content={<ChartTooltip render={tooltipOf} />} />\n {referenceLine && (\n <ReferenceLine y={referenceLine.value} ifOverflow=\"extendDomain\" stroke={colors.axis} strokeDasharray=\"4 4\" />\n )}\n {drawnSeries.map((item) => (\n <Line key={item.key} type=\"monotone\" dataKey={item.key} name={item.label} stroke={item.color}\n strokeWidth={LINE_WIDTH_PIXELS} strokeDasharray={item.dash} isAnimationActive={false}\n dot={{r: DOT_RADIUS_PIXELS, fill: item.color, strokeWidth: 0}}\n activeDot={{r: ACTIVE_DOT_RADIUS_PIXELS, cursor: onSelect ? 'pointer' : 'default'}} />\n ))}\n </RechartsLineChart>\n </ResponsiveContainer>\n </Box>\n )}\n <ChartLegend series={drawnSeries} showsDash ariaLabel={t('charts.legend')} />\n <ReferenceCaption referenceLine={referenceLine} />\n </Box>\n );\n}\n"],"names":["RechartsLineChart"],"mappings":";;;;;;;;;;;AA6BA,MAAM,oBAAoB;AAC1B,MAAM,oBAAoB;AAC1B,MAAM,2BAA2B;AAoBjC,SAAwB,UAAU;AAAA,EAC9B;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAa;AAAA,EAAW;AAAA,EAAY,SAAS;AAAA,EAAK,aAAa;AAAA,EAAiB;AAAA,EAAe;AAAA,EAAU;AAAA,EAAe;AAAA,EAAO,cAAc,WAAW;AAAA,EAAO;AACjL,GAAG;AACC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,aAAA,IAAgB,cAAA;AACvB,QAAM,cAAc,mBAAmB;AACvC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,gBAAgB,WAAW,KAAK;AACvE,QAAM,aAAa,MAAM;AACrB,eAAW,CAAC,OAAO;AACnB,mBAAe,UAAU,WAAW,QAAQ,WAAW,KAAK;AAAA,EAChE;AACA,QAAM,SAAS,eAAA;AACf,QAAM,cAAc,iBAAiB,QAAQ,OAAO,OAAO;AAG3D,MAAI,CAAC,KAAK,QAAQ;AACd,+BAAQ,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAkB,UAAA,YAAW;AAAA,EAC1E;AAEA,QAAM,eAAe,eAAe,MAAM,aAAa,aAAa;AAEpE,QAAM,iBAAiB,CAAC,gBAAgB;AACpC,UAAM,MAAM,KAAK,OAAO,WAAW,CAAC;AACpC,QAAI,KAAK;AACL,eAAS,GAAG;AAAA,IAChB;AAAA,EACJ;AACA,QAAM,YAAY,kBAAkB,CAAC,QACjC,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,UAAA,EAAQ,cAAI,MAAA,CAAM;AAAA,IAClB,YAAY,IAAI,CAAC,8BAAU,KAAA,EAAoB,UAAA;AAAA,MAAA,KAAK;AAAA,MAAM;AAAA,MAAG,YAAY,IAAI,KAAK,GAAG,CAAC;AAAA,IAAA,EAAA,GAAlD,KAAK,GAA+C,CAAM;AAAA,EAAA,EAAA,CACnG;AAGJ,8BACK,KAAA,EACG,UAAA;AAAA,IAAA,oBAAC,aAAA,EAAY,SAAkB,UAAU,YAAY,OAAc;AAAA,IAClE,UACG,oBAAC,YAAA,EAAW,MAAY,QAAQ,aAAa,aAA0B,aAA0B,OAAA,CAAgB,IAEjH,oBAAC,KAAA,EAAI,MAAK,OAAM,cAAY,WAAW,IAAI,EAAC,OAAA,GACxC,UAAA,oBAAC,qBAAA,EAAoB,OAAM,QAAO,QAAO,QAGrC,UAAA;AAAA,MAACA;AAAAA,MAAA;AAAA,QAAkB;AAAA,QAAY,QAAQ,EAAC,KAAK,GAAG,OAAO,IAAI,QAAQ,GAAG,MAAM,GAAA;AAAA,QACzD,SAAS,WAAW,CAAC,UAAU,eAAe,OAAO,WAAW,IAAI;AAAA,QACnF,UAAA;AAAA,UAAA,oBAAC,eAAA,EAAc,UAAU,OAAO,QAAQ,OAAO,MAAM;AAAA,UACrD;AAAA,YAAC;AAAA,YAAA;AAAA,cAAM,SAAQ;AAAA,cAAQ,MAAM,OAAO;AAAA,cAAU,UAAU;AAAA,cAAO,UAAU,EAAC,QAAQ,OAAO,KAAA;AAAA,cAClF,UAAS;AAAA,cAAmB,YAAY;AAAA,YAAA;AAAA,UAAA;AAAA,UAC/C;AAAA,YAAC;AAAA,YAAA;AAAA,cAAM,eAAe;AAAA,cAAO,MAAM,OAAO;AAAA,cAAU,UAAU;AAAA,cAAO,UAAU;AAAA,cACxE,OAAO,iBAAiB,cAAc,WAAW;AAAA,cAAG,eAAe,CAAC,UAAU,YAAY,KAAK;AAAA,YAAA;AAAA,UAAA;AAAA,UACtG,oBAAC,SAAA,EAAQ,QAAQ,EAAC,QAAQ,OAAO,KAAA,GAAO,SAAS,oBAAC,cAAA,EAAa,QAAQ,WAAW,EAAA,CAAI;AAAA,UACrF,iBACG,oBAAC,eAAA,EAAc,GAAG,cAAc,OAAO,YAAW,gBAAe,QAAQ,OAAO,MAAM,iBAAgB,MAAA,CAAM;AAAA,UAE/G,YAAY,IAAI,CAAC,SACd;AAAA,YAAC;AAAA,YAAA;AAAA,cAAoB,MAAK;AAAA,cAAW,SAAS,KAAK;AAAA,cAAK,MAAM,KAAK;AAAA,cAAO,QAAQ,KAAK;AAAA,cACjF,aAAa;AAAA,cAAmB,iBAAiB,KAAK;AAAA,cAAM,mBAAmB;AAAA,cAC/E,KAAK,EAAC,GAAG,mBAAmB,MAAM,KAAK,OAAO,aAAa,EAAA;AAAA,cAC3D,WAAW,EAAC,GAAG,0BAA0B,QAAQ,WAAW,YAAY,UAAA;AAAA,YAAS;AAAA,YAH5E,KAAK;AAAA,UAAA,CAInB;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,GAET,EAAA,CACJ;AAAA,IAEJ,oBAAC,eAAY,QAAQ,aAAa,WAAS,MAAC,WAAW,EAAE,eAAe,GAAG;AAAA,IAC3E,oBAAC,oBAAiB,cAAA,CAA8B;AAAA,EAAA,GACpD;AAER;"}
1
+ {"version":3,"file":"LineChart.js","sources":["../../../src/ui/metrics/LineChart.jsx"],"sourcesContent":["// Líneas a lo largo del tiempo, de una serie o de varias que se comparan (ventas contra devoluciones, este año contra el\n// pasado), dibujadas con Recharts. Lo que comparte con ColumnChart (ejes, tabla, leyenda, línea de referencia) vive en\n// chartParts.jsx.\n//\n// Las reglas de la guía de Apple (HIG «Charts» y «Charting data»):\n// - líneas para ver la tendencia; columnas (ColumnChart) para comparar periodos sueltos;\n// - cada serie con su color Y su trazo (continuo, rayado, punteado), con su punto en cada periodo: el color no va solo;\n// - una serie sin leyenda (el título ya dice qué es); varias, con leyenda abajo; toda gráfica con su vista de tabla;\n// - el globo de un periodo dice el valor de cada serie.\n//\n// <LineChart data={[{key: '2026-4', label: 'abr', sales: 120, returns: 8}, …]} periodLabel=\"Mes\"\n// series={[{key: 'sales', label: 'Ventas'}, {key: 'returns', label: 'Devoluciones', tone: 'error'}]}\n// ariaLabel=\"Ventas y devoluciones por mes\" emptyLabel=\"Sin datos\" />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaLineChart con defaultProps y\n// styleOverrides de sus partes: chart.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport {CartesianGrid, Line, LineChart as RechartsLineChart, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ChartTooltip from '@/ui/metrics/ChartTooltip.jsx';\nimport TableToggle, {CHART_VIEW} from '@/ui/metrics/TableToggle.jsx';\nimport {\n ChartLegend, ChartTable, ReferenceCaption, highestValueOf, resolvedSeriesOf, useChartColors, valueAxisWidthOf,\n} from '@/ui/metrics/chartParts.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaLineChart)\nconst THEME_NAME = 'CalcitaLineChart';\nconst LineChartPart = piecePart(THEME_NAME, 'Chart', Box);\n\n// el grueso de la línea y el tamaño de su punto (px): se ven en el teléfono sin tapar a la vecina\nconst LINE_WIDTH_PIXELS = 2;\nconst DOT_RADIUS_PIXELS = 3;\nconst ACTIVE_DOT_RADIUS_PIXELS = 5;\n\n/**\n * @param {object} props\n * @param {Array<Record<string, any>>} props.data un renglón por periodo: {key, label, [llave de cada serie]: número}\n * @param {Array<{key: string, label: string, tone?: 'primary'|'secondary'|'success'|'warning'|'error'|'info', dash?: string}>} props.series\n * la llave de cada serie en data, su nombre ya traducido, su color y su trazo (por omisión, en orden)\n * @param {string} props.periodLabel encabezado de la primera columna de la tabla («Mes»), ya traducido\n * @param {string} props.ariaLabel\n * @param {string} props.emptyLabel\n * @param {number} [props.height]\n * @param {(value: number) => string} [props.formatValue] cómo se escribe un valor (en el eje, en la tabla y en el globo)\n * @param {{value: number, label: string}} [props.referenceLine] línea de referencia (la meta) con su rótulo ya traducido\n * @param {(row: any) => void} [props.onSelect] recibe el renglón de data del periodo que se picó\n * @param {(row: any) => import('react').ReactNode} [props.renderTooltip] lo que dice el globo de un periodo; por omisión su\n * nombre y el valor de cada serie\n * @param {'chart'|'table'} [props.defaultView] cómo se ve al abrir (CHART_VIEW); por omisión dibujada\n * @param {(view: 'chart'|'table') => void} [props.onViewChange] la persona cambió a la tabla o a la gráfica\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {charts: {viewAsTable, viewAsChart, legend}}\n */\nexport default function LineChart(props) {\n // lo que el proyecto puso en su tema (components.CalcitaLineChart.defaultProps) y encima lo que llega aquí\n const {\n data, series, periodLabel, ariaLabel, emptyLabel, height = 220, formatValue: formatValueProp, referenceLine, onSelect, renderTooltip, texts, defaultView = CHART_VIEW.chart, onViewChange,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const {formatNumber} = useFormatters();\n const formatValue = formatValueProp ?? formatNumber;\n const [asTable, setAsTable] = useState(defaultView === CHART_VIEW.table);\n const toggleView = () => {\n setAsTable(!asTable);\n onViewChange?.(asTable ? CHART_VIEW.chart : CHART_VIEW.table);\n };\n const colors = useChartColors();\n const drawnSeries = resolvedSeriesOf(series, colors.palette);\n\n // guard clause: sin datos se dice, no se pinta un eje vacío\n if (!data.length) {\n return <Typography variant=\"body2\" color=\"text.secondary\">{emptyLabel}</Typography>;\n }\n\n const highestValue = highestValueOf(data, drawnSeries, referenceLine);\n // el periodo que se picó: recharts da su posición en data (a veces como texto)\n const selectPeriodAt = (activeIndex) => {\n const row = data[Number(activeIndex)];\n if (row) {\n onSelect(row);\n }\n };\n const tooltipOf = renderTooltip ?? ((row) => (\n <>\n <strong>{row.label}</strong>\n {drawnSeries.map((item) => <Box key={item.key}>{item.label}: {formatValue(row[item.key])}</Box>)}\n </>\n ));\n\n return (\n <Box>\n <TableToggle asTable={asTable} onToggle={toggleView} texts={texts} />\n {asTable ? (\n <ChartTable data={data} series={drawnSeries} periodLabel={periodLabel} formatValue={formatValue} height={height} />\n ) : (\n <LineChartPart role=\"img\" aria-label={ariaLabel} ownSx={{height}}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n {/* OJO: recharts 3 avisa el clic con el índice del periodo (activeIndex), ya no con activePayload (el de\n la versión 2) */}\n <RechartsLineChart data={data} margin={{top: 8, right: 12, bottom: 0, left: -8}}\n onClick={onSelect ? (state) => selectPeriodAt(state?.activeIndex) : undefined}>\n <CartesianGrid vertical={false} stroke={colors.grid} />\n <XAxis dataKey=\"label\" tick={colors.axisText} tickLine={false} axisLine={{stroke: colors.grid}}\n interval=\"preserveStartEnd\" minTickGap={12} />\n <YAxis allowDecimals={false} tick={colors.axisText} tickLine={false} axisLine={false}\n width={valueAxisWidthOf(highestValue, formatValue)} tickFormatter={(value) => formatValue(value)} />\n <Tooltip cursor={{stroke: colors.grid}} content={<ChartTooltip render={tooltipOf} />} />\n {referenceLine && (\n <ReferenceLine y={referenceLine.value} ifOverflow=\"extendDomain\" stroke={colors.axis} strokeDasharray=\"4 4\" />\n )}\n {drawnSeries.map((item) => (\n <Line key={item.key} type=\"monotone\" dataKey={item.key} name={item.label} stroke={item.color}\n strokeWidth={LINE_WIDTH_PIXELS} strokeDasharray={item.dash} isAnimationActive={false}\n dot={{r: DOT_RADIUS_PIXELS, fill: item.color, strokeWidth: 0}}\n activeDot={{r: ACTIVE_DOT_RADIUS_PIXELS, cursor: onSelect ? 'pointer' : 'default'}} />\n ))}\n </RechartsLineChart>\n </ResponsiveContainer>\n </LineChartPart>\n )}\n <ChartLegend series={drawnSeries} showsDash ariaLabel={t('charts.legend')} />\n <ReferenceCaption referenceLine={referenceLine} />\n </Box>\n );\n}\n"],"names":["RechartsLineChart"],"mappings":"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"}
@@ -11,6 +11,8 @@ import Stack from "@mui/material/Stack";
11
11
  import Typography from "@mui/material/Typography";
12
12
  import { alpha } from "@mui/material/styles";
13
13
  import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
14
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
15
+ import { lightDarkStylesOf } from "../../theme/lightDarkStyles.js";
14
16
  import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
15
17
  import RemoveIcon from "@mui/icons-material/Remove";
16
18
  const TONES = ["info", "success", "warning", "error"];
@@ -23,6 +25,16 @@ const TREND_ICONS = Object.freeze({
23
25
  const TITLE_ROW_MIN_HEIGHT_PIXELS = 24;
24
26
  const ICON_BOX_PIXELS = 28;
25
27
  const ICON_PIXELS = 18;
28
+ const THEME_NAME = "CalcitaMetricCard";
29
+ const CardRoot = piecePart(THEME_NAME, "Root", Paper);
30
+ const CardAction = piecePart(THEME_NAME, "Action", CardActionArea);
31
+ const CardHeader = piecePart(THEME_NAME, "Header", Stack);
32
+ const CardIcon = piecePart(THEME_NAME, "Icon", Box);
33
+ const CardLabel = piecePart(THEME_NAME, "Label", Typography);
34
+ const CardValue = piecePart(THEME_NAME, "Value", Typography);
35
+ const CardCaption = piecePart(THEME_NAME, "Caption", Typography);
36
+ const CardExtra = piecePart(THEME_NAME, "Extra", Box);
37
+ const CardTrend = piecePart(THEME_NAME, "Trend", Chip);
26
38
  function TrendChip({ trend, texts }) {
27
39
  const { t } = useToolkitTranslation("metricCard", texts);
28
40
  const direction = TREND_ICONS[trend.direction] ? trend.direction : TREND_DIRECTION.flat;
@@ -30,7 +42,7 @@ function TrendChip({ trend, texts }) {
30
42
  const isGood = trend.isGood ?? direction === TREND_DIRECTION.up;
31
43
  const color = direction === TREND_DIRECTION.flat ? "default" : isGood ? "success" : "error";
32
44
  return /* @__PURE__ */ jsx(
33
- Chip,
45
+ CardTrend,
34
46
  {
35
47
  size: "small",
36
48
  color,
@@ -38,30 +50,31 @@ function TrendChip({ trend, texts }) {
38
50
  icon: /* @__PURE__ */ jsx(TrendIcon, {}),
39
51
  label: trend.label,
40
52
  "aria-label": `${t(`trend.${direction}`)} ${trend.label}`,
41
- sx: { fontWeight: 600, "& .MuiChip-icon": { fontSize: 16 } }
53
+ ownSx: { fontWeight: 600, "& .MuiChip-icon": { fontSize: 16 } }
42
54
  }
43
55
  );
44
56
  }
45
- function MetricCard({
46
- label,
47
- value,
48
- caption,
49
- tone = "info",
50
- valueColor,
51
- isLoading = false,
52
- to,
53
- trend,
54
- icon,
55
- children,
56
- onClick,
57
- texts
58
- }) {
57
+ function MetricCard(props) {
58
+ const {
59
+ label,
60
+ value,
61
+ caption,
62
+ tone = "info",
63
+ valueColor,
64
+ isLoading = false,
65
+ to,
66
+ trend,
67
+ icon,
68
+ children,
69
+ onClick,
70
+ texts
71
+ } = usePieceProps(props, THEME_NAME);
59
72
  const { Link } = useToolkitNavigation();
60
73
  const paletteTone = TONES.includes(tone) ? tone : "info";
61
74
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [
62
- /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center", justifyContent: "space-between", minHeight: TITLE_ROW_MIN_HEIGHT_PIXELS }, children: [
75
+ /* @__PURE__ */ jsxs(CardHeader, { direction: "row", spacing: 1, ownSx: { alignItems: "center", justifyContent: "space-between", minHeight: TITLE_ROW_MIN_HEIGHT_PIXELS }, children: [
63
76
  /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center", minWidth: 0 }, children: [
64
- icon && /* @__PURE__ */ jsx(Box, { "aria-hidden": "true", sx: (theme) => ({
77
+ icon && /* @__PURE__ */ jsx(CardIcon, { "aria-hidden": "true", ownSx: (theme) => ({
65
78
  width: ICON_BOX_PIXELS,
66
79
  height: ICON_BOX_PIXELS,
67
80
  flexShrink: 0,
@@ -73,22 +86,27 @@ function MetricCard({
73
86
  // el tono al 14 %: se distingue del papel sin competir con la cifra
74
87
  backgroundColor: theme.vars ? `rgba(${theme.vars.palette[paletteTone].mainChannel} / 0.14)` : alpha(theme.palette[paletteTone].main, 0.14)
75
88
  }), children: icon }),
76
- /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "text.secondary", component: "p", sx: { minWidth: 0 }, children: label })
89
+ /* @__PURE__ */ jsx(CardLabel, { variant: "overline", color: "text.secondary", component: "p", ownSx: { minWidth: 0 }, children: label })
77
90
  ] }),
78
91
  trend && !isLoading && /* @__PURE__ */ jsx(TrendChip, { trend, texts })
79
92
  ] }),
80
- /* @__PURE__ */ jsx(Typography, { variant: "h5", component: "p", sx: { mt: 0.75, fontWeight: 600, fontVariantNumeric: "tabular-nums", color: valueColor ?? `${paletteTone}.dark` }, children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { width: 120 }) : value }),
81
- caption && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", component: "p", sx: { mt: 0.5 }, children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { width: 160 }) : caption }),
82
- children && /* @__PURE__ */ jsx(Box, { sx: { mt: 1.5 }, children })
93
+ /* @__PURE__ */ jsx(CardValue, { variant: "h5", component: "p", ownSx: (theme) => ({
94
+ mt: 0.75,
95
+ fontWeight: 600,
96
+ fontVariantNumeric: "tabular-nums",
97
+ ...valueColor ? { color: valueColor } : lightDarkStylesOf(theme, { color: (theme.vars ?? theme).palette[paletteTone].dark }, { color: (theme.vars ?? theme).palette[paletteTone].light })
98
+ }), children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { width: 120 }) : value }),
99
+ caption && /* @__PURE__ */ jsx(CardCaption, { variant: "caption", color: "text.secondary", component: "p", ownSx: { mt: 0.5 }, children: isLoading ? /* @__PURE__ */ jsx(Skeleton, { width: 160 }) : caption }),
100
+ children && /* @__PURE__ */ jsx(CardExtra, { ownSx: { mt: 1.5 }, children })
83
101
  ] });
84
102
  const isClickable = Boolean(to || onClick);
85
- return /* @__PURE__ */ jsx(Paper, { variant: "outlined", sx: {
103
+ return /* @__PURE__ */ jsx(CardRoot, { variant: "outlined", ownSx: {
86
104
  borderTop: 3,
87
105
  borderTopColor: `${paletteTone}.light`,
88
106
  height: "100%",
89
107
  overflow: "hidden",
90
108
  ...isClickable ? {} : { p: 2.5 }
91
- }, children: isClickable ? /* @__PURE__ */ jsx(CardActionArea, { ...to ? { component: Link, to } : { onClick }, sx: { p: 2.5, height: "100%", alignItems: "flex-start", display: "block" }, children: content }) : content });
109
+ }, children: isClickable ? /* @__PURE__ */ jsx(CardAction, { ...to ? { component: Link, to } : { onClick }, ownSx: { p: 2.5, height: "100%", alignItems: "flex-start", display: "block" }, children: content }) : content });
92
110
  }
93
111
  export {
94
112
  TREND_DIRECTION,
@@ -1 +1 @@
1
- {"version":3,"file":"MetricCard.js","sources":["../../../src/ui/metrics/MetricCard.jsx"],"sourcesContent":["// Tarjeta de métrica: etiqueta en versalitas, número grande (h5 en seminegritas, con cifras del mismo ancho) y una nota abajo, con un borde superior de\n// color según el tono (p. ej. REGISTROS LEÍDOS / ÍNTEGROS / DESCARTADOS).\n//\n// Con `to` la tarjeta entera es una liga a la pantalla que explica el número, ya filtrada: un KPI que no lleva a\n// ningún lado obliga a buscar a mano de dónde salió.\n//\n// Con `trend` lleva junto al número cuánto cambió contra el periodo anterior («▲ 12 %», como los tableros de MUI). El\n// color dice si es bueno o malo, no si subió: más cancelaciones es subir y es malo (isGood: false). La flecha y el texto\n// oculto dicen la dirección, para quien no distingue el color.\n//\n// Se extiende sin tocar el framework: un ícono junto al título (icon, del color del tono en un cuadro suave, como los\n// resúmenes de Salud de Apple), contenido propio debajo de la nota (children: una barra de avance, un desglose), onClick\n// cuando no lleva a otra pantalla, y texts para los textos de la tendencia.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport CardActionArea from '@mui/material/CardActionArea';\nimport Chip from '@mui/material/Chip';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport {alpha} from '@mui/material/styles';\nimport ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';\nimport ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';\nimport RemoveIcon from '@mui/icons-material/Remove';\n\nconst TONES = ['info', 'success', 'warning', 'error'];\n\n/** Hacia dónde se movió el número. */\nexport const TREND_DIRECTION = Object.freeze({up: 'up', down: 'down', flat: 'flat'});\n\nconst TREND_ICONS = Object.freeze({\n [TREND_DIRECTION.up]: ArrowUpwardIcon,\n [TREND_DIRECTION.down]: ArrowDownwardIcon,\n [TREND_DIRECTION.flat]: RemoveIcon,\n});\n\n// el renglón del título: del alto de la píldora de tendencia, para que las tarjetas con y sin tendencia queden parejas\nconst TITLE_ROW_MIN_HEIGHT_PIXELS = 24;\n// el cuadro del ícono junto al título y el tamaño del ícono adentro (px)\nconst ICON_BOX_PIXELS = 28;\nconst ICON_PIXELS = 18;\n\n/**\n * El cambio contra el periodo anterior: verde si es bueno, rojo si es malo, gris si no cambió.\n *\n * @param {{trend: {label: string, direction: string, isGood?: boolean}, texts?: object}} props\n */\nfunction TrendChip({trend, texts}) {\n const {t} = useToolkitTranslation('metricCard', texts);\n const direction = TREND_ICONS[trend.direction] ? trend.direction : TREND_DIRECTION.flat;\n const TrendIcon = TREND_ICONS[direction];\n // por omisión subir es bueno; quien cuente algo malo (cancelaciones, quejas) lo dice con isGood\n const isGood = trend.isGood ?? direction === TREND_DIRECTION.up;\n const color = direction === TREND_DIRECTION.flat ? 'default' : (isGood ? 'success' : 'error');\n return (\n <Chip size=\"small\" color={color} variant=\"outlined\" icon={<TrendIcon />} label={trend.label}\n aria-label={`${t(`trend.${direction}`)} ${trend.label}`} sx={{fontWeight: 600, '& .MuiChip-icon': {fontSize: 16}}} />\n );\n}\n\n/**\n * @param {object} props\n * @param {string} props.label texto ya traducido\n * @param {import('react').ReactNode} props.value ya formateado (formatNumber)\n * @param {import('react').ReactNode} [props.caption]\n * @param {'info'|'success'|'warning'|'error'} [props.tone]\n * @param {string} [props.valueColor] color del número si no debe ir del tono (p. ej. un total en texto normal)\n * @param {boolean} [props.isLoading]\n * @param {string} [props.to] ruta interna a la que lleva la tarjeta\n * @param {{label: string, direction: 'up'|'down'|'flat', isGood?: boolean}} [props.trend] cuánto cambió (TREND_DIRECTION)\n * @param {import('react').ReactNode} [props.icon] junto al título, del color del tono (<ShoppingBagOutlinedIcon />)\n * @param {import('react').ReactNode} [props.children] contenido propio debajo de la nota (una barra de avance, un desglose)\n * @param {() => void} [props.onClick] la tarjeta entera como botón, cuando no lleva a otra pantalla (to)\n * @param {object} [props.texts] los textos que cambian, con la forma de `metricCard`: {trend: {up: 'subió'}}\n */\nexport default function MetricCard({\n label, value, caption, tone = 'info', valueColor, isLoading = false, to, trend, icon, children, onClick, texts,\n}) {\n // el enlace del router que conectó el proyecto (ToolkitProvider)\n const {Link} = useToolkitNavigation();\n const paletteTone = TONES.includes(tone) ? tone : 'info';\n\n const content = (\n <>\n {/* la tendencia va en el renglón del título, a la derecha: en el mismo lugar en todas las tarjetas aunque la cifra\n sea larga (junto a la cifra, con un importe largo se bajaba a otro renglón y no quedaban parejas; lo reportaron) */}\n <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center', justifyContent: 'space-between', minHeight: TITLE_ROW_MIN_HEIGHT_PIXELS}}>\n <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center', minWidth: 0}}>\n {icon && (\n <Box aria-hidden=\"true\" sx={(theme) => ({\n width: ICON_BOX_PIXELS, height: ICON_BOX_PIXELS, flexShrink: 0, display: 'grid', placeItems: 'center',\n borderRadius: 1.5, color: `${paletteTone}.main`, '& svg': {fontSize: ICON_PIXELS},\n // el tono al 14 %: se distingue del papel sin competir con la cifra\n backgroundColor: theme.vars\n ? `rgba(${theme.vars.palette[paletteTone].mainChannel} / 0.14)`\n : alpha(theme.palette[paletteTone].main, 0.14),\n })}>\n {icon}\n </Box>\n )}\n <Typography variant=\"overline\" color=\"text.secondary\" component=\"p\" sx={{minWidth: 0}}>{label}</Typography>\n </Stack>\n {trend && !isLoading && <TrendChip trend={trend} texts={texts} />}\n </Stack>\n <Typography variant=\"h5\" component=\"p\" sx={{mt: 0.75, fontWeight: 600, fontVariantNumeric: 'tabular-nums', color: valueColor ?? `${paletteTone}.dark`}}>\n {isLoading ? <Skeleton width={120} /> : value}\n </Typography>\n {caption && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\" sx={{mt: 0.5}}>\n {isLoading ? <Skeleton width={160} /> : caption}\n </Typography>\n )}\n {children && <Box sx={{mt: 1.5}}>{children}</Box>}\n </>\n );\n // una liga a la pantalla que explica el número, o un botón (onClick); sin ninguno, solo se lee\n const isClickable = Boolean(to || onClick);\n\n return (\n <Paper variant=\"outlined\" sx={{\n borderTop: 3,\n borderTopColor: `${paletteTone}.light`,\n height: '100%',\n overflow: 'hidden',\n ...(isClickable ? {} : {p: 2.5}),\n }}>\n {isClickable ? (\n <CardActionArea {...(to ? {component: Link, to} : {onClick})} sx={{p: 2.5, height: '100%', alignItems: 'flex-start', display: 'block'}}>\n {content}\n </CardActionArea>\n ) : content}\n </Paper>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA6BA,MAAM,QAAQ,CAAC,QAAQ,WAAW,WAAW,OAAO;AAG7C,MAAM,kBAAkB,OAAO,OAAO,EAAC,IAAI,MAAM,MAAM,QAAQ,MAAM,OAAA,CAAO;AAEnF,MAAM,cAAc,OAAO,OAAO;AAAA,EAC9B,CAAC,gBAAgB,EAAE,GAAG;AAAA,EACtB,CAAC,gBAAgB,IAAI,GAAG;AAAA,EACxB,CAAC,gBAAgB,IAAI,GAAG;AAC5B,CAAC;AAGD,MAAM,8BAA8B;AAEpC,MAAM,kBAAkB;AACxB,MAAM,cAAc;AAOpB,SAAS,UAAU,EAAC,OAAO,SAAQ;AAC/B,QAAM,EAAC,EAAA,IAAK,sBAAsB,cAAc,KAAK;AACrD,QAAM,YAAY,YAAY,MAAM,SAAS,IAAI,MAAM,YAAY,gBAAgB;AACnF,QAAM,YAAY,YAAY,SAAS;AAEvC,QAAM,SAAS,MAAM,UAAU,cAAc,gBAAgB;AAC7D,QAAM,QAAQ,cAAc,gBAAgB,OAAO,YAAa,SAAS,YAAY;AACrF,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAK,MAAK;AAAA,MAAQ;AAAA,MAAc,SAAQ;AAAA,MAAW,0BAAO,WAAA,EAAU;AAAA,MAAI,OAAO,MAAM;AAAA,MAChF,cAAY,GAAG,EAAE,SAAS,SAAS,EAAE,CAAC,IAAI,MAAM,KAAK;AAAA,MAAI,IAAI,EAAC,YAAY,KAAK,mBAAmB,EAAC,UAAU,KAAE;AAAA,IAAC;AAAA,EAAA;AAE9H;AAiBA,SAAwB,WAAW;AAAA,EAC/B;AAAA,EAAO;AAAA,EAAO;AAAA,EAAS,OAAO;AAAA,EAAQ;AAAA,EAAY,YAAY;AAAA,EAAO;AAAA,EAAI;AAAA,EAAO;AAAA,EAAM;AAAA,EAAU;AAAA,EAAS;AAC7G,GAAG;AAEC,QAAM,EAAC,KAAA,IAAQ,qBAAA;AACf,QAAM,cAAc,MAAM,SAAS,IAAI,IAAI,OAAO;AAElD,QAAM,UACF,qBAAA,UAAA,EAGI,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,YAAY,UAAU,gBAAgB,iBAAiB,WAAW,+BACtG,UAAA;AAAA,MAAA,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,YAAY,UAAU,UAAU,EAAA,GACnE,UAAA;AAAA,QAAA,4BACI,KAAA,EAAI,eAAY,QAAO,IAAI,CAAC,WAAW;AAAA,UACpC,OAAO;AAAA,UAAiB,QAAQ;AAAA,UAAiB,YAAY;AAAA,UAAG,SAAS;AAAA,UAAQ,YAAY;AAAA,UAC7F,cAAc;AAAA,UAAK,OAAO,GAAG,WAAW;AAAA,UAAS,SAAS,EAAC,UAAU,YAAA;AAAA;AAAA,UAErE,iBAAiB,MAAM,OACjB,QAAQ,MAAM,KAAK,QAAQ,WAAW,EAAE,WAAW,aACnD,MAAM,MAAM,QAAQ,WAAW,EAAE,MAAM,IAAI;AAAA,QAAA,IAEhD,UAAA,MACL;AAAA,QAEJ,oBAAC,YAAA,EAAW,SAAQ,YAAW,OAAM,kBAAiB,WAAU,KAAI,IAAI,EAAC,UAAU,EAAA,GAAK,UAAA,MAAA,CAAM;AAAA,MAAA,GAClG;AAAA,MACC,SAAS,CAAC,aAAa,oBAAC,WAAA,EAAU,OAAc,MAAA,CAAc;AAAA,IAAA,GACnE;AAAA,IACA,oBAAC,YAAA,EAAW,SAAQ,MAAK,WAAU,KAAI,IAAI,EAAC,IAAI,MAAM,YAAY,KAAK,oBAAoB,gBAAgB,OAAO,cAAc,GAAG,WAAW,QAAA,GACzI,UAAA,YAAY,oBAAC,UAAA,EAAS,OAAO,IAAA,CAAK,IAAK,MAAA,CAC5C;AAAA,IACC,+BACI,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAI,IAAI,EAAC,IAAI,OACvE,UAAA,YAAY,oBAAC,YAAS,OAAO,IAAA,CAAK,IAAK,SAC5C;AAAA,IAEH,gCAAa,KAAA,EAAI,IAAI,EAAC,IAAI,IAAA,GAAO,SAAA,CAAS;AAAA,EAAA,GAC/C;AAGJ,QAAM,cAAc,QAAQ,MAAM,OAAO;AAEzC,SACI,oBAAC,OAAA,EAAM,SAAQ,YAAW,IAAI;AAAA,IAC1B,WAAW;AAAA,IACX,gBAAgB,GAAG,WAAW;AAAA,IAC9B,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,GAAI,cAAc,CAAA,IAAK,EAAC,GAAG,IAAA;AAAA,EAAG,GAE7B,UAAA,cACG,oBAAC,gBAAA,EAAgB,GAAI,KAAK,EAAC,WAAW,MAAM,OAAM,EAAC,QAAA,GAAW,IAAI,EAAC,GAAG,KAAK,QAAQ,QAAQ,YAAY,cAAc,SAAS,QAAA,GACzH,UAAA,QAAA,CACL,IACA,QAAA,CACR;AAER;"}
1
+ {"version":3,"file":"MetricCard.js","sources":["../../../src/ui/metrics/MetricCard.jsx"],"sourcesContent":["// Tarjeta de métrica: etiqueta en versalitas, número grande (h5 en seminegritas, con cifras del mismo ancho) y una nota abajo, con un borde superior de\n// color según el tono (p. ej. REGISTROS LEÍDOS / ÍNTEGROS / DESCARTADOS).\n//\n// Con `to` la tarjeta entera es una liga a la pantalla que explica el número, ya filtrada: un KPI que no lleva a\n// ningún lado obliga a buscar a mano de dónde salió.\n//\n// Con `trend` lleva junto al número cuánto cambió contra el periodo anterior («▲ 12 %», como los tableros de MUI). El\n// color dice si es bueno o malo, no si subió: más cancelaciones es subir y es malo (isGood: false). La flecha y el texto\n// oculto dicen la dirección, para quien no distingue el color.\n//\n// Se extiende sin tocar el framework: un ícono junto al título (icon, del color del tono en un cuadro suave, como los\n// resúmenes de Salud de Apple), contenido propio debajo de la nota (children: una barra de avance, un desglose), onClick\n// cuando no lleva a otra pantalla, y texts para los textos de la tendencia.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaMetricCard con defaultProps y\n// styleOverrides de sus partes: root, action, header, icon, label, value, caption, extra y trend.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport CardActionArea from '@mui/material/CardActionArea';\nimport Chip from '@mui/material/Chip';\nimport Paper from '@mui/material/Paper';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport {alpha} from '@mui/material/styles';\nimport ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {lightDarkStylesOf} from '@/theme/lightDarkStyles.js';\nimport ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';\nimport RemoveIcon from '@mui/icons-material/Remove';\n\nconst TONES = ['info', 'success', 'warning', 'error'];\n\n/** Hacia dónde se movió el número. */\nexport const TREND_DIRECTION = Object.freeze({up: 'up', down: 'down', flat: 'flat'});\n\nconst TREND_ICONS = Object.freeze({\n [TREND_DIRECTION.up]: ArrowUpwardIcon,\n [TREND_DIRECTION.down]: ArrowDownwardIcon,\n [TREND_DIRECTION.flat]: RemoveIcon,\n});\n\n// el renglón del título: del alto de la píldora de tendencia, para que las tarjetas con y sin tendencia queden parejas\nconst TITLE_ROW_MIN_HEIGHT_PIXELS = 24;\n// el cuadro del ícono junto al título y el tamaño del ícono adentro (px)\nconst ICON_BOX_PIXELS = 28;\nconst ICON_PIXELS = 18;\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaMetricCard)\nconst THEME_NAME = 'CalcitaMetricCard';\nconst CardRoot = piecePart(THEME_NAME, 'Root', Paper);\nconst CardAction = piecePart(THEME_NAME, 'Action', CardActionArea);\nconst CardHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst CardIcon = piecePart(THEME_NAME, 'Icon', Box);\nconst CardLabel = piecePart(THEME_NAME, 'Label', Typography);\nconst CardValue = piecePart(THEME_NAME, 'Value', Typography);\nconst CardCaption = piecePart(THEME_NAME, 'Caption', Typography);\nconst CardExtra = piecePart(THEME_NAME, 'Extra', Box);\nconst CardTrend = piecePart(THEME_NAME, 'Trend', Chip);\n\n/**\n * El cambio contra el periodo anterior: verde si es bueno, rojo si es malo, gris si no cambió.\n *\n * @param {{trend: {label: string, direction: string, isGood?: boolean}, texts?: object}} props\n */\nfunction TrendChip({trend, texts}) {\n const {t} = useToolkitTranslation('metricCard', texts);\n const direction = TREND_ICONS[trend.direction] ? trend.direction : TREND_DIRECTION.flat;\n const TrendIcon = TREND_ICONS[direction];\n // por omisión subir es bueno; quien cuente algo malo (cancelaciones, quejas) lo dice con isGood\n const isGood = trend.isGood ?? direction === TREND_DIRECTION.up;\n const color = direction === TREND_DIRECTION.flat ? 'default' : (isGood ? 'success' : 'error');\n return (\n <CardTrend size=\"small\" color={color} variant=\"outlined\" icon={<TrendIcon />} label={trend.label}\n aria-label={`${t(`trend.${direction}`)} ${trend.label}`} ownSx={{fontWeight: 600, '& .MuiChip-icon': {fontSize: 16}}} />\n );\n}\n\n/**\n * @param {object} props\n * @param {string} props.label texto ya traducido\n * @param {import('react').ReactNode} props.value ya formateado (formatNumber)\n * @param {import('react').ReactNode} [props.caption]\n * @param {'info'|'success'|'warning'|'error'} [props.tone]\n * @param {string} [props.valueColor] color del número si no debe ir del tono (p. ej. un total en texto normal)\n * @param {boolean} [props.isLoading]\n * @param {string} [props.to] ruta interna a la que lleva la tarjeta\n * @param {{label: string, direction: 'up'|'down'|'flat', isGood?: boolean}} [props.trend] cuánto cambió (TREND_DIRECTION)\n * @param {import('react').ReactNode} [props.icon] junto al título, del color del tono (<ShoppingBagOutlinedIcon />)\n * @param {import('react').ReactNode} [props.children] contenido propio debajo de la nota (una barra de avance, un desglose)\n * @param {() => void} [props.onClick] la tarjeta entera como botón, cuando no lleva a otra pantalla (to)\n * @param {object} [props.texts] los textos que cambian, con la forma de `metricCard`: {trend: {up: 'subió'}}\n */\nexport default function MetricCard(props) {\n // lo que el proyecto puso en su tema (components.CalcitaMetricCard.defaultProps) y encima lo que llega aquí\n const {\n label, value, caption, tone = 'info', valueColor, isLoading = false, to, trend, icon, children, onClick, texts,\n } = usePieceProps(props, THEME_NAME);\n // el enlace del router que conectó el proyecto (ToolkitProvider)\n const {Link} = useToolkitNavigation();\n const paletteTone = TONES.includes(tone) ? tone : 'info';\n\n const content = (\n <>\n {/* la tendencia va en el renglón del título, a la derecha: en el mismo lugar en todas las tarjetas aunque la cifra\n sea larga (junto a la cifra, con un importe largo se bajaba a otro renglón y no quedaban parejas; lo reportaron) */}\n <CardHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', justifyContent: 'space-between', minHeight: TITLE_ROW_MIN_HEIGHT_PIXELS}}>\n <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center', minWidth: 0}}>\n {icon && (\n <CardIcon aria-hidden=\"true\" ownSx={(theme) => ({\n width: ICON_BOX_PIXELS, height: ICON_BOX_PIXELS, flexShrink: 0, display: 'grid', placeItems: 'center',\n borderRadius: 1.5, color: `${paletteTone}.main`, '& svg': {fontSize: ICON_PIXELS},\n // el tono al 14 %: se distingue del papel sin competir con la cifra\n backgroundColor: theme.vars\n ? `rgba(${theme.vars.palette[paletteTone].mainChannel} / 0.14)`\n : alpha(theme.palette[paletteTone].main, 0.14),\n })}>\n {icon}\n </CardIcon>\n )}\n <CardLabel variant=\"overline\" color=\"text.secondary\" component=\"p\" ownSx={{minWidth: 0}}>{label}</CardLabel>\n </Stack>\n {trend && !isLoading && <TrendChip trend={trend} texts={texts} />}\n </CardHeader>\n {/* la cifra en el tono oscuro de su color en claro y en el claro en oscuro (con el oscuro también en modo oscuro\n casi no se leía); valueColor del proyecto manda en los dos */}\n <CardValue variant=\"h5\" component=\"p\" ownSx={(theme) => ({\n mt: 0.75, fontWeight: 600, fontVariantNumeric: 'tabular-nums',\n ...(valueColor\n ? {color: valueColor}\n : lightDarkStylesOf(theme, {color: (theme.vars ?? theme).palette[paletteTone].dark}, {color: (theme.vars ?? theme).palette[paletteTone].light})),\n })}>\n {isLoading ? <Skeleton width={120} /> : value}\n </CardValue>\n {caption && (\n <CardCaption variant=\"caption\" color=\"text.secondary\" component=\"p\" ownSx={{mt: 0.5}}>\n {isLoading ? <Skeleton width={160} /> : caption}\n </CardCaption>\n )}\n {children && <CardExtra ownSx={{mt: 1.5}}>{children}</CardExtra>}\n </>\n );\n // una liga a la pantalla que explica el número, o un botón (onClick); sin ninguno, solo se lee\n const isClickable = Boolean(to || onClick);\n\n return (\n <CardRoot variant=\"outlined\" ownSx={{\n borderTop: 3,\n borderTopColor: `${paletteTone}.light`,\n height: '100%',\n overflow: 'hidden',\n ...(isClickable ? {} : {p: 2.5}),\n }}>\n {isClickable ? (\n <CardAction {...(to ? {component: Link, to} : {onClick})} ownSx={{p: 2.5, height: '100%', alignItems: 'flex-start', display: 'block'}}>\n {content}\n </CardAction>\n ) : content}\n </CardRoot>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -3,10 +3,15 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
4
4
  import Box from "@mui/material/Box";
5
5
  import Button from "@mui/material/Button";
6
+ import { piecePart, usePieceProps } from "../../theme/themePieces.js";
7
+ const THEME_NAME = "CalcitaTableToggle";
8
+ const ToggleRoot = piecePart(THEME_NAME, "Root", Box);
9
+ const ToggleButton = piecePart(THEME_NAME, "Button", Button);
6
10
  const CHART_VIEW = Object.freeze({ chart: "chart", table: "table" });
7
- function TableToggle({ asTable, onToggle, texts }) {
11
+ function TableToggle(props) {
12
+ const { asTable, onToggle, texts } = usePieceProps(props, THEME_NAME);
8
13
  const { t } = useToolkitTranslation("common", texts);
9
- return /* @__PURE__ */ jsx(Box, { sx: { display: "flex", justifyContent: "flex-end", mb: 1 }, children: /* @__PURE__ */ jsx(Button, { size: "small", onClick: onToggle, sx: { textTransform: "none" }, children: asTable ? t("charts.viewAsChart") : t("charts.viewAsTable") }) });
14
+ return /* @__PURE__ */ jsx(ToggleRoot, { ownSx: { display: "flex", justifyContent: "flex-end", mb: 1 }, children: /* @__PURE__ */ jsx(ToggleButton, { size: "small", onClick: onToggle, ownSx: { textTransform: "none" }, children: asTable ? t("charts.viewAsChart") : t("charts.viewAsTable") }) });
10
15
  }
11
16
  export {
12
17
  CHART_VIEW,
@@ -1 +1 @@
1
- {"version":3,"file":"TableToggle.js","sources":["../../../src/ui/metrics/TableToggle.jsx"],"sourcesContent":["// \"Ver como tabla\": toda gráfica tiene su versión en tabla, para quien no distingue el color, usa lector de pantalla o\n// quiere copiar los números.\n//\n// Los textos salen de i18n.\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';\n\n/** Cómo se ve una gráfica: dibujada o en su tabla (defaultView y onViewChange de BarList, ColumnChart, LineChart y CalendarHeatmap). */\nexport const CHART_VIEW = Object.freeze({chart: 'chart', table: 'table'});\n\n/**\n * @param {{asTable: boolean, onToggle: () => void, texts?: object}} props texts: los textos que cambian, con la forma de\n * `common` ({charts: {viewAsTable, viewAsChart}})\n */\nexport default function TableToggle({asTable, onToggle, texts}) {\n const {t} = useToolkitTranslation('common', texts);\n\n return (\n <Box sx={{display: 'flex', justifyContent: 'flex-end', mb: 1}}>\n <Button size=\"small\" onClick={onToggle} sx={{textTransform: 'none'}}>\n {asTable ? t('charts.viewAsChart') : t('charts.viewAsTable')}\n </Button>\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;AAWO,MAAM,aAAa,OAAO,OAAO,EAAC,OAAO,SAAS,OAAO,SAAQ;AAMxE,SAAwB,YAAY,EAAC,SAAS,UAAU,SAAQ;AAC5D,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,SACI,oBAAC,KAAA,EAAI,IAAI,EAAC,SAAS,QAAQ,gBAAgB,YAAY,IAAI,EAAA,GACvD,UAAA,oBAAC,QAAA,EAAO,MAAK,SAAQ,SAAS,UAAU,IAAI,EAAC,eAAe,OAAA,GACvD,UAAA,UAAU,EAAE,oBAAoB,IAAI,EAAE,oBAAoB,GAC/D,GACJ;AAER;"}
1
+ {"version":3,"file":"TableToggle.js","sources":["../../../src/ui/metrics/TableToggle.jsx"],"sourcesContent":["// \"Ver como tabla\": toda gráfica tiene su versión en tabla, para quien no distingue el color, usa lector de pantalla o\n// quiere copiar los números.\n//\n// Los textos salen de i18n.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTableToggle con defaultProps y\n// styleOverrides de sus partes: root, button.\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 {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaTableToggle)\nconst THEME_NAME = 'CalcitaTableToggle';\nconst ToggleRoot = piecePart(THEME_NAME, 'Root', Box);\nconst ToggleButton = piecePart(THEME_NAME, 'Button', Button);\n\n/** Cómo se ve una gráfica: dibujada o en su tabla (defaultView y onViewChange de BarList, ColumnChart, LineChart y CalendarHeatmap). */\nexport const CHART_VIEW = Object.freeze({chart: 'chart', table: 'table'});\n\n/**\n * @param {{asTable: boolean, onToggle: () => void, texts?: object}} props texts: los textos que cambian, con la forma de\n * `common` ({charts: {viewAsTable, viewAsChart}})\n */\nexport default function TableToggle(props) {\n // lo que el proyecto puso en su tema (components.CalcitaTableToggle.defaultProps) y encima lo que llega aquí\n const {asTable, onToggle, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n\n return (\n <ToggleRoot ownSx={{display: 'flex', justifyContent: 'flex-end', mb: 1}}>\n <ToggleButton size=\"small\" onClick={onToggle} ownSx={{textTransform: 'none'}}>\n {asTable ? t('charts.viewAsChart') : t('charts.viewAsTable')}\n </ToggleButton>\n </ToggleRoot>\n );\n}\n"],"names":[],"mappings":";;;;;;AAeA,MAAM,aAAa;AACnB,MAAM,aAAa,UAAU,YAAY,QAAQ,GAAG;AACpD,MAAM,eAAe,UAAU,YAAY,UAAU,MAAM;AAGpD,MAAM,aAAa,OAAO,OAAO,EAAC,OAAO,SAAS,OAAO,SAAQ;AAMxE,SAAwB,YAAY,OAAO;AAEvC,QAAM,EAAC,SAAS,UAAU,UAAS,cAAc,OAAO,UAAU;AAClE,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,SACI,oBAAC,YAAA,EAAW,OAAO,EAAC,SAAS,QAAQ,gBAAgB,YAAY,IAAI,EAAA,GACjE,UAAA,oBAAC,cAAA,EAAa,MAAK,SAAQ,SAAS,UAAU,OAAO,EAAC,eAAe,OAAA,GAChE,UAAA,UAAU,EAAE,oBAAoB,IAAI,EAAE,oBAAoB,GAC/D,GACJ;AAER;"}