@stackcraftmx/calcita 0.27.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 +149 -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 +7 -1
  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 +66 -63
  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 +10 -6
  125. package/dist/theme/components/minimalComponents.js.map +1 -1
  126. package/dist/theme/createToolkitTheme.js +23 -6
  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 +64 -10
  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 +2 -1
  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 +33 -1
  192. package/dist/types/theme/cssTokens.d.ts +46 -0
  193. package/dist/types/theme/glassSurface.d.ts +57 -1
  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":"FormSection.js","sources":["../../src/inputs/FormSection.jsx"],"sourcesContent":["// El formulario agrupado de Apple (el de Ajustes y el de la hoja «Nuevo evento» de Calendario): una tarjeta redonda con\n// renglones, el título de la sección arriba de la tarjeta y la explicación abajo. Dentro de una FormSection cada campo del\n// framework (TextInput, NumberInput, SelectInput, CatalogInput, ColorInput, DateInput y sus versiones de formik y de\n// react-hook-form) se pinta solo como renglón: la etiqueta a la izquierda y el campo a la derecha.\n//\n// <FormSection title=\"Datos del cliente\" footer=\"Le enviaremos la factura al correo.\">\n// <FormTextField name=\"name\" label=\"Nombre\" />\n// <FormTextField name=\"email\" label=\"Correo\" />\n// <FormRow label=\"Recibir avisos\"><Switch /></FormRow>\n// </FormSection>\n//\n// Las reglas, de la guía de Apple (HIG «Text fields», «Pickers», «Combo boxes» y sus imágenes):\n// - la etiqueta siempre a la vista, a la izquierda (la pista se va al escribir; la etiqueta no);\n// - el campo es un «pozo» parejo con esquinas de 12 px (fieldWellStylesOf): gris tenue en claro y más oscuro que la\n// tarjeta en oscuro, con la letra clara, como el campo de la imagen de Apple. Antes iba transparente y el relleno solo\n// salía con el cursor encima, en cuadro: no se entendía dónde escribir (lo reportaron);\n// - la fecha es el mismo campo que los demás, a todo lo ancho (en cápsula al final del renglón quedaba descuadrada), con\n// la letra del texto (no del color del tema: en oscuro el naranja ahí «se veía horrible», lo reportaron);\n// - al escribir, un aro tenue del color principal y la etiqueta del color del tema; con error, aro y etiqueta en rojo y\n// el mensaje debajo del campo;\n// - una línea fina entre renglones, que empieza donde empieza el texto.\n//\n// Los campos pueden ir directo o dentro de lo que el proyecto necesite (un Grid de dos columnas, un Stack, un componente\n// suyo): cada renglón dibuja su propia línea ARRIBA y la tarjeta sube su contenido un píxel y lo recorta, así la línea del\n// primero queda escondida. OJO: antes la línea se dibujaba entre dos hermanos seguidos y con un Grid en medio desaparecían\n// todas (lo señalaron en una revisión: obligaba a «la forma Calcita»).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Box from '@mui/material/Box';\nimport Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\n\nimport {FormSectionContext} from '@/inputs/formLayoutContext.js';\nimport {GLASS_PANEL_RADIUS_PIXELS, accentTextStylesOf, fieldWellStylesOf} from '@/theme/glassSurface.js';\n\n/** La clase de cada renglón: la sección le dibuja su línea de arriba, esté donde esté adentro de la tarjeta. */\nexport const FORM_ROW_CLASS = 'calcita-form-row';\n\n// el renglón: alto mínimo para el dedo, su aire arriba y abajo, y lo ancho de la columna de las etiquetas\nconst ROW_MIN_HEIGHT_PIXELS = 52;\nconst ROW_PADDING_BLOCK_PIXELS = 8;\nconst LABEL_COLUMN = Object.freeze({share: '38%', maxPixels: 200});\n// lo que la etiqueta baja para quedar en la misma línea que el texto del campo (el campo mide 40 px y su letra 24 px)\nconst LABEL_TOP_PIXELS = 8;\n// el aro al escribir y con error (por ciento del color) y su grosor\nconst RING = Object.freeze({focusPercent: 45, errorPercent: 60, pixels: 2});\n\n/**\n * Los campos de MUI dentro de un renglón: el pozo de Apple (fieldWellStylesOf), sin el borde de MUI ni el vidrio del tema,\n * con su aro al escribir. Los selectores van con «html body &&&» para ganarle a los ajustes del tema dentro de una\n * tarjeta, que con el lente llevan una etiqueta y hasta cuatro clases (con menos, el relleno del tema salía al pasar el\n * cursor, en cuadro: los «hovers espantosos» que reportaron).\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction rowControlSxOf(theme) {\n const palette = (theme.vars ?? theme).palette;\n const strong = (selectors) => selectors.map((selector) => `html body &&& ${selector}`).join(', ');\n const roots = ['.MuiOutlinedInput-root', '.MuiFilledInput-root', '.MuiPickersOutlinedInput-root'];\n const ringOf = (color, percent) => ({outline: `${RING.pixels}px solid color-mix(in srgb, ${color} ${percent}%, transparent)`, outlineOffset: 0});\n return {\n [strong(roots)]: {...fieldWellStylesOf(theme), outline: 'none'},\n [strong(roots.map((root) => `${root}.Mui-focused`))]: ringOf(palette.primary.main, RING.focusPercent),\n [strong(roots.map((root) => `${root}.Mui-error`))]: ringOf(palette.error.main, RING.errorPercent),\n [strong(['.MuiOutlinedInput-notchedOutline', '.MuiPickersOutlinedInput-notchedOutline'])]: {display: 'none'},\n [strong(['.MuiFilledInput-root::before', '.MuiFilledInput-root::after'])]: {display: 'none'},\n // la fecha: el mismo campo que los demás, a todo lo ancho de su columna (en cápsula al final del renglón quedaba\n // descuadrada, con su ayuda a la izquierda debajo de nada; lo reportaron), con el ícono del calendario discreto\n [strong(['.MuiPickersOutlinedInput-root'])]: {color: palette.text.primary},\n [strong(['.MuiPickersOutlinedInput-root .MuiInputAdornment-root .MuiSvgIcon-root'])]: {fontSize: '1.125rem', color: palette.text.secondary},\n // la ayuda y el error, debajo del campo; el renglón no guarda lugar para una ayuda vacía\n '& .MuiFormHelperText-root': {marginInline: theme.spacing(0.5), marginTop: '4px', marginBottom: '2px'},\n '& .MuiFormHelperText-root:has(> .notranslate:only-child)': {display: 'none'},\n };\n}\n\n/**\n * Un renglón del formulario agrupado: la etiqueta a la izquierda y lo que sea a la derecha (un campo, un interruptor, un\n * texto). Los campos del framework lo usan solos dentro de una FormSection; FormRow suelto es para lo demás.\n *\n * @param {object} props\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {string} [props.htmlFor] el id del campo que nombra la etiqueta\n * @param {string} [props.labelId] el id de la etiqueta (para un campo que la nombra con aria-labelledby)\n * @param {import('react').ReactNode} props.children\n */\nexport function FormRow({label, htmlFor, labelId, children}) {\n return (\n <Box className={FORM_ROW_CLASS}\n sx={(theme) => {\n const palette = (theme.vars ?? theme).palette;\n return {\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-start',\n gap: 2,\n minHeight: ROW_MIN_HEIGHT_PIXELS,\n px: 2,\n py: `${ROW_PADDING_BLOCK_PIXELS}px`,\n '&:focus-within > label': accentTextStylesOf(theme),\n '&:has(.Mui-error) > label': {color: palette.error.main},\n ...rowControlSxOf(theme),\n };\n }}>\n <Typography component=\"label\" id={labelId} htmlFor={htmlFor} variant=\"body1\"\n sx={{flex: `0 0 ${LABEL_COLUMN.share}`, maxWidth: LABEL_COLUMN.maxPixels, pt: `${LABEL_TOP_PIXELS}px`, lineHeight: 1.5, color: 'text.primary'}}>\n {label}\n </Typography>\n <Box sx={{flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', minHeight: 40}}>\n {children}\n </Box>\n </Box>\n );\n}\n\n/**\n * @param {object} props\n * @param {string} [props.title] ya traducido; va arriba de la tarjeta\n * @param {import('react').ReactNode} [props.footer] la explicación, abajo de la tarjeta\n * @param {import('react').ReactNode} props.children los campos, directo o dentro de un Grid, un Stack o un componente propio\n */\nexport default function FormSection({title, footer, children}) {\n const titleId = useId();\n return (\n <FormSectionContext value>\n <Box component=\"section\" aria-labelledby={title ? titleId : undefined}>\n {title && (\n <Typography id={titleId} variant=\"subtitle2\" component=\"h2\" color=\"text.secondary\" sx={{px: 2, mb: 0.75, fontWeight: 600}}>\n {title}\n </Typography>\n )}\n <Paper variant=\"outlined\"\n sx={(theme) => ({\n borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`,\n overflow: 'hidden',\n // la línea de arriba de cada renglón, desde donde empieza el texto (la del primero la recorta la tarjeta)\n [`& .${FORM_ROW_CLASS}::before`]: {\n content: '\"\"', position: 'absolute', top: 0, right: 0, left: theme.spacing(2), height: '1px',\n backgroundColor: (theme.vars ?? theme).palette.divider,\n },\n })}>\n {/* un píxel arriba: la línea de los renglones de hasta arriba queda fuera de la tarjeta (overflow hidden) */}\n <Box sx={{mt: '-1px'}}>{children}</Box>\n </Paper>\n {footer && (\n <Typography variant=\"caption\" component=\"p\" color=\"text.secondary\" sx={{px: 2, mt: 0.75}}>{footer}</Typography>\n )}\n </Box>\n </FormSectionContext>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FormSection.js","sources":["../../src/inputs/FormSection.jsx"],"sourcesContent":["// El formulario agrupado de Apple (el de Ajustes y el de la hoja «Nuevo evento» de Calendario): una tarjeta redonda con\n// renglones, el título de la sección arriba de la tarjeta y la explicación abajo. Dentro de una FormSection cada campo del\n// framework (TextInput, NumberInput, SelectInput, CatalogInput, ColorInput, DateInput y sus versiones de formik y de\n// react-hook-form) se pinta solo como renglón: la etiqueta a la izquierda y el campo a la derecha.\n//\n// <FormSection title=\"Datos del cliente\" footer=\"Le enviaremos la factura al correo.\">\n// <FormTextField name=\"name\" label=\"Nombre\" />\n// <FormTextField name=\"email\" label=\"Correo\" />\n// <FormRow label=\"Recibir avisos\"><Switch /></FormRow>\n// </FormSection>\n//\n// Las reglas, de la guía de Apple (HIG «Text fields», «Pickers», «Combo boxes» y sus imágenes):\n// - la etiqueta siempre a la vista, a la izquierda (la pista se va al escribir; la etiqueta no);\n// - el campo es un «pozo» parejo con esquinas de 12 px (fieldWellStylesOf): gris tenue en claro y más claro que la\n// tarjeta en oscuro, con la letra clara, como el campo de la imagen de Apple. Antes iba transparente y el relleno solo\n// salía con el cursor encima, en cuadro: no se entendía dónde escribir (lo reportaron);\n// - la fecha es el mismo campo que los demás, a todo lo ancho (en cápsula al final del renglón quedaba descuadrada), con\n// la letra del texto (no del color del tema: en oscuro el naranja ahí «se veía horrible», lo reportaron);\n// - al escribir, un aro tenue del color principal y la etiqueta del color del tema; con error, aro y etiqueta en rojo y\n// el mensaje debajo del campo;\n// - una línea fina entre renglones, que empieza donde empieza el texto.\n//\n// Los campos pueden ir directo o dentro de lo que el proyecto necesite (un Grid de dos columnas, un Stack, un componente\n// suyo): cada renglón dibuja su propia línea ARRIBA y la tarjeta sube su contenido un píxel y lo recorta, así la línea del\n// primero queda escondida. OJO: antes la línea se dibujaba entre dos hermanos seguidos y con un Grid en medio desaparecían\n// todas (lo señalaron en una revisión: obligaba a «la forma Calcita»).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFormSection (sus partes root, title, card y\n// footer) y components.CalcitaFormRow (root, label y control), con styleOverrides y defaultProps. Los colores de los campos,\n// más fácil con createToolkitTheme({fields}).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Box from '@mui/material/Box';\nimport Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\nimport {styled, useThemeProps} from '@mui/material/styles';\n\nimport {FormSectionContext} from '@/inputs/formLayoutContext.js';\nimport {PANEL_RADIUS, accentTextStylesOf, fieldVariablesOf, fieldWellFromVariablesOf} from '@/theme/glassSurface.js';\n\n/** La clase de cada renglón: la sección le dibuja su línea de arriba, esté donde esté adentro de la tarjeta. */\nexport const FORM_ROW_CLASS = 'calcita-form-row';\n\n// el renglón: alto mínimo para el dedo, su aire arriba y abajo, y lo ancho de la columna de las etiquetas\nconst ROW_MIN_HEIGHT_PIXELS = 52;\nconst ROW_PADDING_BLOCK_PIXELS = 8;\nconst LABEL_COLUMN = Object.freeze({share: '38%', maxPixels: 200});\n// lo que la etiqueta baja para quedar en la misma línea que el texto del campo (el campo mide 40 px y su letra 24 px)\nconst LABEL_TOP_PIXELS = 8;\n// el aro al escribir y con error (por ciento del color) y su grosor\nconst RING = Object.freeze({focusPercent: 45, errorPercent: 60, pixels: 2});\n\n/**\n * Los campos de MUI dentro de un renglón: el pozo de Apple (fieldWellStylesOf), sin el borde de MUI ni el vidrio del tema,\n * con su aro al escribir. Los selectores van con «html body &&&» para ganarle a los ajustes del tema dentro de una\n * tarjeta, que con el lente llevan una etiqueta y hasta cuatro clases (con menos, el relleno del tema salía al pasar el\n * cursor, en cuadro: los «hovers espantosos» que reportaron).\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction rowControlSxOf(theme) {\n const palette = (theme.vars ?? theme).palette;\n const strong = (selectors) => selectors.map((selector) => `html body &&& ${selector}`).join(', ');\n const roots = ['.MuiOutlinedInput-root', '.MuiFilledInput-root', '.MuiPickersOutlinedInput-root'];\n const ringOf = (color, percent) => ({outline: `${RING.pixels}px solid color-mix(in srgb, ${color} ${percent}%, transparent)`, outlineOffset: 0});\n return {\n // los tonos van en variables del renglón (fieldVariablesOf, abajo en FormRow): aquí abajo una regla de oscuro no aplica\n [strong(roots)]: {...fieldWellFromVariablesOf(theme), outline: 'none'},\n [strong(roots.map((root) => `${root}.Mui-focused`))]: ringOf(palette.primary.main, RING.focusPercent),\n [strong(roots.map((root) => `${root}.Mui-error`))]: ringOf(palette.error.main, RING.errorPercent),\n [strong(['.MuiOutlinedInput-notchedOutline', '.MuiPickersOutlinedInput-notchedOutline'])]: {display: 'none'},\n [strong(['.MuiFilledInput-root::before', '.MuiFilledInput-root::after'])]: {display: 'none'},\n // la fecha: el mismo campo que los demás, a todo lo ancho de su columna (en cápsula al final del renglón quedaba\n // descuadrada, con su ayuda a la izquierda debajo de nada; lo reportaron), con el ícono del calendario discreto\n [strong(['.MuiPickersOutlinedInput-root'])]: {color: palette.text.primary},\n [strong(['.MuiPickersOutlinedInput-root .MuiInputAdornment-root .MuiSvgIcon-root'])]: {fontSize: '1.125rem', color: palette.text.secondary},\n // la ayuda y el error, debajo del campo; el renglón no guarda lugar para una ayuda vacía\n '& .MuiFormHelperText-root': {marginInline: theme.spacing(0.5), marginTop: '4px', marginBottom: '2px'},\n '& .MuiFormHelperText-root:has(> .notranslate:only-child)': {display: 'none'},\n };\n}\n\n// los nombres de las piezas en el tema (theme.components.CalcitaFormSection y CalcitaFormRow)\nconst SECTION_THEME_NAME = 'CalcitaFormSection';\nconst ROW_THEME_NAME = 'CalcitaFormRow';\n\n// el renglón: la etiqueta a la izquierda y el campo a la derecha, con la línea de arriba y los tonos de los campos\nconst FormRowRoot = styled(Box, {name: ROW_THEME_NAME, slot: 'Root'})(({theme}) => {\n const palette = (theme.vars ?? theme).palette;\n return theme.unstable_sx({\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-start',\n gap: 2,\n minHeight: ROW_MIN_HEIGHT_PIXELS,\n px: 2,\n py: `${ROW_PADDING_BLOCK_PIXELS}px`,\n '&:focus-within > label': accentTextStylesOf(theme),\n '&:has(.Mui-error) > label': {color: palette.error.main},\n // los tonos de los campos (los del proyecto con createToolkitTheme fields, o los de Apple) para sus campos\n ...fieldVariablesOf(theme),\n ...rowControlSxOf(theme),\n });\n});\n\nconst FormRowLabel = styled(Typography, {name: ROW_THEME_NAME, slot: 'Label'})(({theme}) => theme.unstable_sx({\n flex: `0 0 ${LABEL_COLUMN.share}`, maxWidth: LABEL_COLUMN.maxPixels, pt: `${LABEL_TOP_PIXELS}px`, lineHeight: 1.5, color: 'text.primary',\n}));\n\nconst FormRowControl = styled(Box, {name: ROW_THEME_NAME, slot: 'Control'})({\n flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', minHeight: 40,\n});\n\nconst FormSectionRoot = styled(Box, {name: SECTION_THEME_NAME, slot: 'Root'})({});\n\nconst FormSectionTitle = styled(Typography, {name: SECTION_THEME_NAME, slot: 'Title'})(({theme}) => theme.unstable_sx({px: 2, mb: 0.75, fontWeight: 600}));\n\n// la tarjeta: la línea de arriba de cada renglón, desde donde empieza el texto (la del primero la recorta la tarjeta)\nconst FormSectionCard = styled(Paper, {name: SECTION_THEME_NAME, slot: 'Card'})(({theme}) => ({\n borderRadius: PANEL_RADIUS,\n overflow: 'hidden',\n [`& .${FORM_ROW_CLASS}::before`]: {\n content: '\"\"', position: 'absolute', top: 0, right: 0, left: theme.spacing(2), height: '1px',\n backgroundColor: (theme.vars ?? theme).palette.divider,\n },\n}));\n\nconst FormSectionFooter = styled(Typography, {name: SECTION_THEME_NAME, slot: 'Footer'})(({theme}) => theme.unstable_sx({px: 2, mt: 0.75}));\n\n/**\n * Un renglón del formulario agrupado: la etiqueta a la izquierda y lo que sea a la derecha (un campo, un interruptor, un\n * texto). Los campos del framework lo usan solos dentro de una FormSection; FormRow suelto es para lo demás.\n *\n * @param {object} props\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {string} [props.htmlFor] el id del campo que nombra la etiqueta\n * @param {string} [props.labelId] el id de la etiqueta (para un campo que la nombra con aria-labelledby)\n * @param {import('react').ReactNode} props.children\n */\nexport function FormRow(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFormRow.defaultProps) y encima lo que llega aquí\n const {label, htmlFor, labelId, children} = useThemeProps({props, name: ROW_THEME_NAME});\n return (\n <FormRowRoot className={FORM_ROW_CLASS}>\n <FormRowLabel component=\"label\" id={labelId} htmlFor={htmlFor} variant=\"body1\">\n {label}\n </FormRowLabel>\n <FormRowControl>\n {children}\n </FormRowControl>\n </FormRowRoot>\n );\n}\n\n/**\n * @param {object} props\n * @param {string} [props.title] ya traducido; va arriba de la tarjeta\n * @param {import('react').ReactNode} [props.footer] la explicación, abajo de la tarjeta\n * @param {import('react').ReactNode} props.children los campos, directo o dentro de un Grid, un Stack o un componente propio\n */\nexport default function FormSection(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFormSection.defaultProps) y encima lo que llega aquí\n const {title, footer, children} = useThemeProps({props, name: SECTION_THEME_NAME});\n const titleId = useId();\n return (\n <FormSectionContext value>\n <FormSectionRoot component=\"section\" aria-labelledby={title ? titleId : undefined}>\n {title && (\n <FormSectionTitle id={titleId} variant=\"subtitle2\" component=\"h2\" color=\"text.secondary\">\n {title}\n </FormSectionTitle>\n )}\n <FormSectionCard variant=\"outlined\">\n {/* un píxel arriba: la línea de los renglones de hasta arriba queda fuera de la tarjeta (overflow hidden) */}\n <Box sx={{mt: '-1px'}}>{children}</Box>\n </FormSectionCard>\n {footer && (\n <FormSectionFooter variant=\"caption\" component=\"p\" color=\"text.secondary\">{footer}</FormSectionFooter>\n )}\n </FormSectionRoot>\n </FormSectionContext>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -8,21 +8,26 @@ import TextField from "@mui/material/TextField";
8
8
  import Typography from "@mui/material/Typography";
9
9
  import { translateValidationMessage } from "../forms/validationMessages.js";
10
10
  import FieldFrame from "./FieldFrame.js";
11
- function SelectInput({
12
- name,
13
- label,
14
- value,
15
- onChange,
16
- onBlur,
17
- options,
18
- error,
19
- helperText = " ",
20
- placeholder,
21
- disabled = false,
22
- inputRef,
23
- multiple = false,
24
- texts
25
- }) {
11
+ import { piecePart, usePieceProps } from "../theme/themePieces.js";
12
+ const THEME_NAME = "CalcitaSelectInput";
13
+ const SelectOption = piecePart(THEME_NAME, "Option", MenuItem);
14
+ const SelectOptionText = piecePart(THEME_NAME, "OptionText", Box);
15
+ function SelectInput(props) {
16
+ const {
17
+ name,
18
+ label,
19
+ value,
20
+ onChange,
21
+ onBlur,
22
+ options,
23
+ error,
24
+ helperText = " ",
25
+ placeholder,
26
+ disabled = false,
27
+ inputRef,
28
+ multiple = false,
29
+ texts
30
+ } = usePieceProps(props, THEME_NAME);
26
31
  const { t } = useToolkitTranslation("common", texts);
27
32
  const generatedId = useId();
28
33
  const inputId = `${name ?? "select"}-${generatedId}`;
@@ -69,7 +74,7 @@ function SelectInput({
69
74
  options.map((option) => (
70
75
  // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del
71
76
  // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)
72
- /* @__PURE__ */ jsx(MenuItem, { value: option.value, sx: { whiteSpace: "normal" }, children: /* @__PURE__ */ jsxs(Box, { component: "span", sx: { display: "flex", flexDirection: "column", minWidth: 0, flexGrow: 1 }, children: [
77
+ /* @__PURE__ */ jsx(SelectOption, { value: option.value, ownSx: { whiteSpace: "normal" }, children: /* @__PURE__ */ jsxs(SelectOptionText, { component: "span", ownSx: { display: "flex", flexDirection: "column", minWidth: 0, flexGrow: 1 }, children: [
73
78
  /* @__PURE__ */ jsx(Typography, { component: "span", variant: "body2", children: option.label }),
74
79
  option.description && /* @__PURE__ */ jsx(Typography, { component: "span", variant: "caption", color: "text.secondary", children: option.description })
75
80
  ] }) }, option.value)
@@ -1 +1 @@
1
- {"version":3,"file":"SelectInput.js","sources":["../../src/inputs/SelectInput.jsx"],"sourcesContent":["// Lista desplegable suelta, con la etiqueta arriba (mismo look que TextInput): la que pintan FormSelectField (formik) y la\n// de react-hook-form, y la que se usa sin formulario.\n//\n// Cada opción puede traer una explicación (description) que se lee en la lista para escoger; en la caja queda solo el\n// nombre de lo elegido. OJO: sin renderValue, MUI copiaba la opción completa a la caja y se leía «MenudeoCompra por pieza…».\n//\n// Con `multiple` se eligen varias: la lista se queda abierta, cada elegida lleva su palomita y en la caja quedan sus nombres\n// separados por coma, en el orden de las opciones. Al formulario llega la lista de valores (Yup.array().min(1) la valida).\n// Para catálogos largos, CatalogInput con `multiple` (busca y deja chips).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport MenuItem from '@mui/material/MenuItem';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|Array<string|number>|null|undefined} props.value '' = sin elegir; con multiple, la lista ([] = ninguna)\n * @param {(value: any) => void} props.onChange recibe el valor elegido (con multiple, la lista), no el evento\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string}>} props.options\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] texto de la opción vacía\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias opciones\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SelectInput({\n name, label, value, onChange, onBlur, options, error, helperText = ' ', placeholder, disabled = false, inputRef, multiple = false, texts,\n}) {\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'select'}-${generatedId}`;\n // OJO: la lista desplegable de MUI es un div y no un <input>: el `for` de la etiqueta no la une. Se nombra con su etiqueta\n // por aria-labelledby (labelId); sin esto el lector de pantalla no decía «Ciudad», solo lo elegido\n const labelId = `${inputId}-label`;\n const errorText = translateValidationMessage(t, error);\n const placeholderText = (\n <Typography component=\"span\"\n // OJO: el placeholder de un select activo se lee (text.secondary); text.disabled solo con el campo inactivo\n color={disabled ? 'text.disabled' : 'text.secondary'}>{placeholder ?? t('form.selectPlaceholder')}</Typography>\n );\n const renderSelectedValue = (selectedValue) => {\n if (multiple) {\n const selectedLabels = options.filter((option) => selectedValue.includes(option.value)).map((option) => option.label);\n return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholderText;\n }\n return options.find((option) => option.value === selectedValue)?.label ?? placeholderText;\n };\n const currentValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? '');\n // OJO: con multiple, el autollenado del navegador puede mandar el valor como texto separado por comas (lo dice MUI)\n const handleChange = (event) => {\n const nextValue = event.target.value;\n onChange(multiple && typeof nextValue === 'string' ? nextValue.split(',') : nextValue);\n };\n\n return (\n <FieldFrame inputId={inputId} labelId={labelId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n select\n size=\"small\"\n fullWidth\n disabled={disabled}\n value={currentValue}\n onChange={handleChange}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n slotProps={{select: {multiple, displayEmpty: true, renderValue: renderSelectedValue, labelId}}}>\n {/* la opción vacía solo con una: con varias, se quitan picándolas otra vez */}\n {!multiple && <MenuItem value=\"\" disabled>{placeholderText}</MenuItem>}\n {options.map((option) => (\n // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del\n // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)\n <MenuItem key={option.value} value={option.value} sx={{whiteSpace: 'normal'}}>\n <Box component=\"span\" sx={{display: 'flex', flexDirection: 'column', minWidth: 0, flexGrow: 1}}>\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n {option.description && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n )}\n </Box>\n </MenuItem>\n ))}\n </TextField>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAqCA,SAAwB,YAAY;AAAA,EAChC;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAO,aAAa;AAAA,EAAK;AAAA,EAAa,WAAW;AAAA,EAAO;AAAA,EAAU,WAAW;AAAA,EAAO;AACvI,GAAG;AACC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,QAAQ,IAAI,WAAW;AAGlD,QAAM,UAAU,GAAG,OAAO;AAC1B,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,kBACF;AAAA,IAAC;AAAA,IAAA;AAAA,MAAW,WAAU;AAAA,MAEV,OAAO,WAAW,kBAAkB;AAAA,MAAmB,UAAA,eAAe,EAAE,wBAAwB;AAAA,IAAA;AAAA,EAAA;AAEhH,QAAM,sBAAsB,CAAC,kBAAkB;AAC3C,QAAI,UAAU;AACV,YAAM,iBAAiB,QAAQ,OAAO,CAAC,WAAW,cAAc,SAAS,OAAO,KAAK,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK;AACpH,aAAO,eAAe,SAAS,IAAI,eAAe,KAAK,IAAI,IAAI;AAAA,IACnE;AACA,WAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,aAAa,GAAG,SAAS;AAAA,EAC9E;AACA,QAAM,eAAe,WAAY,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAO,SAAS;AAEhF,QAAM,eAAe,CAAC,UAAU;AAC5B,UAAM,YAAY,MAAM,OAAO;AAC/B,aAAS,YAAY,OAAO,cAAc,WAAW,UAAU,MAAM,GAAG,IAAI,SAAS;AAAA,EACzF;AAEA,SACI,oBAAC,YAAA,EAAW,SAAkB,SAAkB,OAC5C,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA,QAAM;AAAA,MACN,MAAK;AAAA,MACL,WAAS;AAAA,MACT;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,MACV;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,aAAa;AAAA,MACzB,WAAW,EAAC,QAAQ,EAAC,UAAU,cAAc,MAAM,aAAa,qBAAqB,UAAO;AAAA,MAE3F,UAAA;AAAA,QAAA,CAAC,YAAY,oBAAC,UAAA,EAAS,OAAM,IAAG,UAAQ,MAAE,UAAA,iBAAgB;AAAA,QAC1D,QAAQ,IAAI,CAAC;AAAA;AAAA;AAAA,UAGV,oBAAC,UAAA,EAA4B,OAAO,OAAO,OAAO,IAAI,EAAC,YAAY,SAAA,GAC/D,UAAA,qBAAC,KAAA,EAAI,WAAU,QAAO,IAAI,EAAC,SAAS,QAAQ,eAAe,UAAU,UAAU,GAAG,UAAU,EAAA,GACxF,UAAA;AAAA,YAAA,oBAAC,cAAW,WAAU,QAAO,SAAQ,SAAS,iBAAO,OAAM;AAAA,YAC1D,OAAO,eACJ,oBAAC,YAAA,EAAW,WAAU,QAAO,SAAQ,WAAU,OAAM,kBAAkB,UAAA,OAAO,YAAA,CAAY;AAAA,UAAA,GAElG,EAAA,GANW,OAAO,KAOtB;AAAA,SACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAET;AAER;"}
1
+ {"version":3,"file":"SelectInput.js","sources":["../../src/inputs/SelectInput.jsx"],"sourcesContent":["// Lista desplegable suelta, con la etiqueta arriba (mismo look que TextInput): la que pintan FormSelectField (formik) y la\n// de react-hook-form, y la que se usa sin formulario.\n//\n// Cada opción puede traer una explicación (description) que se lee en la lista para escoger; en la caja queda solo el\n// nombre de lo elegido. OJO: sin renderValue, MUI copiaba la opción completa a la caja y se leía «MenudeoCompra por pieza…».\n//\n// Con `multiple` se eligen varias: la lista se queda abierta, cada elegida lleva su palomita y en la caja quedan sus nombres\n// separados por coma, en el orden de las opciones. Al formulario llega la lista de valores (Yup.array().min(1) la valida).\n// Para catálogos largos, CatalogInput con `multiple` (busca y deja chips).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSelectInput con defaultProps y\n// styleOverrides de sus partes: option, optionText.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport MenuItem from '@mui/material/MenuItem';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaSelectInput)\nconst THEME_NAME = 'CalcitaSelectInput';\nconst SelectOption = piecePart(THEME_NAME, 'Option', MenuItem);\nconst SelectOptionText = piecePart(THEME_NAME, 'OptionText', Box);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|Array<string|number>|null|undefined} props.value '' = sin elegir; con multiple, la lista ([] = ninguna)\n * @param {(value: any) => void} props.onChange recibe el valor elegido (con multiple, la lista), no el evento\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string}>} props.options\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] texto de la opción vacía\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias opciones\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SelectInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSelectInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, options, error, helperText = ' ', placeholder, disabled = false, inputRef, multiple = false, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'select'}-${generatedId}`;\n // OJO: la lista desplegable de MUI es un div y no un <input>: el `for` de la etiqueta no la une. Se nombra con su etiqueta\n // por aria-labelledby (labelId); sin esto el lector de pantalla no decía «Ciudad», solo lo elegido\n const labelId = `${inputId}-label`;\n const errorText = translateValidationMessage(t, error);\n const placeholderText = (\n <Typography component=\"span\"\n // OJO: el placeholder de un select activo se lee (text.secondary); text.disabled solo con el campo inactivo\n color={disabled ? 'text.disabled' : 'text.secondary'}>{placeholder ?? t('form.selectPlaceholder')}</Typography>\n );\n const renderSelectedValue = (selectedValue) => {\n if (multiple) {\n const selectedLabels = options.filter((option) => selectedValue.includes(option.value)).map((option) => option.label);\n return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholderText;\n }\n return options.find((option) => option.value === selectedValue)?.label ?? placeholderText;\n };\n const currentValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? '');\n // OJO: con multiple, el autollenado del navegador puede mandar el valor como texto separado por comas (lo dice MUI)\n const handleChange = (event) => {\n const nextValue = event.target.value;\n onChange(multiple && typeof nextValue === 'string' ? nextValue.split(',') : nextValue);\n };\n\n return (\n <FieldFrame inputId={inputId} labelId={labelId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n select\n size=\"small\"\n fullWidth\n disabled={disabled}\n value={currentValue}\n onChange={handleChange}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n slotProps={{select: {multiple, displayEmpty: true, renderValue: renderSelectedValue, labelId}}}>\n {/* la opción vacía solo con una: con varias, se quitan picándolas otra vez */}\n {!multiple && <MenuItem value=\"\" disabled>{placeholderText}</MenuItem>}\n {options.map((option) => (\n // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del\n // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)\n <SelectOption key={option.value} value={option.value} ownSx={{whiteSpace: 'normal'}}>\n <SelectOptionText component=\"span\" ownSx={{display: 'flex', flexDirection: 'column', minWidth: 0, flexGrow: 1}}>\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n {option.description && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n )}\n </SelectOptionText>\n </SelectOption>\n ))}\n </TextField>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AA0BA,MAAM,aAAa;AACnB,MAAM,eAAe,UAAU,YAAY,UAAU,QAAQ;AAC7D,MAAM,mBAAmB,UAAU,YAAY,cAAc,GAAG;AAkBhE,SAAwB,YAAY,OAAO;AAEvC,QAAM;AAAA,IACN;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAa,WAAW;AAAA,IAAO;AAAA,IAAU,WAAW;AAAA,IAAO;AAAA,EAAA,IACnI,cAAc,OAAO,UAAU;AAC/B,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,QAAQ,IAAI,WAAW;AAGlD,QAAM,UAAU,GAAG,OAAO;AAC1B,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,kBACF;AAAA,IAAC;AAAA,IAAA;AAAA,MAAW,WAAU;AAAA,MAEV,OAAO,WAAW,kBAAkB;AAAA,MAAmB,UAAA,eAAe,EAAE,wBAAwB;AAAA,IAAA;AAAA,EAAA;AAEhH,QAAM,sBAAsB,CAAC,kBAAkB;AAC3C,QAAI,UAAU;AACV,YAAM,iBAAiB,QAAQ,OAAO,CAAC,WAAW,cAAc,SAAS,OAAO,KAAK,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK;AACpH,aAAO,eAAe,SAAS,IAAI,eAAe,KAAK,IAAI,IAAI;AAAA,IACnE;AACA,WAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,aAAa,GAAG,SAAS;AAAA,EAC9E;AACA,QAAM,eAAe,WAAY,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAO,SAAS;AAEhF,QAAM,eAAe,CAAC,UAAU;AAC5B,UAAM,YAAY,MAAM,OAAO;AAC/B,aAAS,YAAY,OAAO,cAAc,WAAW,UAAU,MAAM,GAAG,IAAI,SAAS;AAAA,EACzF;AAEA,SACI,oBAAC,YAAA,EAAW,SAAkB,SAAkB,OAC5C,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA,QAAM;AAAA,MACN,MAAK;AAAA,MACL,WAAS;AAAA,MACT;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,MACV;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,aAAa;AAAA,MACzB,WAAW,EAAC,QAAQ,EAAC,UAAU,cAAc,MAAM,aAAa,qBAAqB,UAAO;AAAA,MAE3F,UAAA;AAAA,QAAA,CAAC,YAAY,oBAAC,UAAA,EAAS,OAAM,IAAG,UAAQ,MAAE,UAAA,iBAAgB;AAAA,QAC1D,QAAQ,IAAI,CAAC;AAAA;AAAA;AAAA,UAGV,oBAAC,cAAA,EAAgC,OAAO,OAAO,OAAO,OAAO,EAAC,YAAY,SAAA,GACtE,UAAA,qBAAC,kBAAA,EAAiB,WAAU,QAAO,OAAO,EAAC,SAAS,QAAQ,eAAe,UAAU,UAAU,GAAG,UAAU,EAAA,GACxG,UAAA;AAAA,YAAA,oBAAC,cAAW,WAAU,QAAO,SAAQ,SAAS,iBAAO,OAAM;AAAA,YAC1D,OAAO,eACJ,oBAAC,YAAA,EAAW,WAAU,QAAO,SAAQ,WAAU,OAAM,kBAAkB,UAAA,OAAO,YAAA,CAAY;AAAA,UAAA,GAElG,EAAA,GANe,OAAO,KAO1B;AAAA,SACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAET;AAER;"}
@@ -11,6 +11,9 @@ import VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined
11
11
  import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined";
12
12
  import { translateValidationMessage } from "../forms/validationMessages.js";
13
13
  import FieldFrame from "./FieldFrame.js";
14
+ import { piecePart, usePieceProps } from "../theme/themePieces.js";
15
+ const THEME_NAME = "CalcitaTextInput";
16
+ const InputField = piecePart(THEME_NAME, "Field", TextField);
14
17
  const CLEAR_BUTTON_CLASS = "calcita-clear-button";
15
18
  const CLEAR_BUTTON_SX = Object.freeze({
16
19
  [`& .${CLEAR_BUTTON_CLASS}`]: { visibility: "hidden", color: "text.secondary" },
@@ -19,24 +22,25 @@ const CLEAR_BUTTON_SX = Object.freeze({
19
22
  // una captura de Tareas). Se esconde la del navegador; la de la casa es la que se ve igual en todos
20
23
  '& input[type="search"]::-webkit-search-cancel-button': { WebkitAppearance: "none", display: "none" }
21
24
  });
22
- function TextInput({
23
- name,
24
- label,
25
- value,
26
- onChange,
27
- onBlur,
28
- error,
29
- type = "text",
30
- helperText = " ",
31
- startAdornment,
32
- endAdornment: customEndAdornment,
33
- uppercase = false,
34
- normalizeOnBlur,
35
- inputRef,
36
- clearable = true,
37
- texts,
38
- ...textFieldProps
39
- }) {
25
+ function TextInput(props) {
26
+ const {
27
+ name,
28
+ label,
29
+ value,
30
+ onChange,
31
+ onBlur,
32
+ error,
33
+ type = "text",
34
+ helperText = " ",
35
+ startAdornment,
36
+ endAdornment: customEndAdornment,
37
+ uppercase = false,
38
+ normalizeOnBlur,
39
+ inputRef,
40
+ clearable = true,
41
+ texts,
42
+ ...textFieldProps
43
+ } = usePieceProps(props, THEME_NAME);
40
44
  const { t } = useToolkitTranslation("common", texts);
41
45
  const [isRevealed, setIsRevealed] = useState(false);
42
46
  const generatedId = useId();
@@ -71,7 +75,7 @@ function TextInput({
71
75
  onBlur?.();
72
76
  };
73
77
  return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, children: /* @__PURE__ */ jsx(
74
- TextField,
78
+ InputField,
75
79
  {
76
80
  ...textFieldProps,
77
81
  name,
@@ -81,7 +85,8 @@ function TextInput({
81
85
  onChange: (event) => onChange(uppercase ? event.target.value.toUpperCase() : event.target.value),
82
86
  onBlur: handleBlur,
83
87
  type: isPassword && isRevealed ? "text" : type,
84
- sx: [CLEAR_BUTTON_SX, ...[].concat(textFieldProps.sx ?? [])],
88
+ ownSx: CLEAR_BUTTON_SX,
89
+ sx: textFieldProps.sx,
85
90
  error: Boolean(errorText),
86
91
  helperText: errorText ?? helperText,
87
92
  fullWidth: true,
@@ -1 +1 @@
1
- {"version":3,"file":"TextInput.js","sources":["../../src/inputs/TextInput.jsx"],"sourcesContent":["// Campo de texto suelto, con la etiqueta arriba: el que pintan FormTextField (formik) y el de react-hook-form, y el que se\n// usa sin formulario (controlado: value / onChange).\n//\n// Lo que resuelve, y por lo que existe en vez de un <TextField> suelto:\n// - el renglón de ayuda está reservado desde el principio, así la tarjeta no brinca cuando aparece un error;\n// - las contraseñas traen el ojo para revelarlas (con aria-label traducido);\n// - con texto, la ⓧ para borrarlo de un toque, como los campos de Apple («Display a Clear button in the trailing end of\n// a text field», HIG «Text fields»). Sale con el cursor encima o al escribir, para que un formulario lleno no se vea\n// lleno de ⓧ; no entra al recorrido del teclado (con el teclado se borra seleccionando el texto);\n// - normalizeOnBlur deja el valor como lo va a mandar el formulario (el correo en minúsculas y sin espacios) al salir del\n// campo: la persona ve lo que se envía;\n// - uppercase normaliza a mayúsculas mientras se escribe (claves, RFC);\n// - el error llega como LLAVE de yup ({key, values}) o como texto ya traducido (el del servidor) y se pinta en el idioma\n// activo. Cuándo enseñarlo (ya se tocó, ya se envió) lo decide quien lo conecta al formulario.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport CancelIcon from '@mui/icons-material/Cancel';\nimport VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';\nimport VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\n\n// la ⓧ de borrar: escondida hasta que el campo tiene el cursor encima o se está escribiendo en él\nconst CLEAR_BUTTON_CLASS = 'calcita-clear-button';\nconst CLEAR_BUTTON_SX = Object.freeze({\n [`& .${CLEAR_BUTTON_CLASS}`]: {visibility: 'hidden', color: 'text.secondary'},\n [`& .MuiInputBase-root:hover .${CLEAR_BUTTON_CLASS}, & .MuiInputBase-root.Mui-focused .${CLEAR_BUTTON_CLASS}`]: {visibility: 'visible'},\n // un campo de búsqueda (type=\"search\") trae la × del navegador: con la ⓧ de la casa salían dos para lo mismo (lo cachó\n // una captura de Tareas). Se esconde la del navegador; la de la casa es la que se ve igual en todos\n '& input[type=\"search\"]::-webkit-search-cancel-button': {WebkitAppearance: 'none', display: 'none'},\n});\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value\n * @param {(value: string) => void} props.onChange recibe el texto, no el evento\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error] el error a enseñar (undefined = sin error)\n * @param {string} [props.type] 'password' agrega el ojo\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] ya traducido; el ejemplo de qué escribir (se va al escribir)\n * @param {import('react').ReactNode} [props.startAdornment]\n * @param {import('react').ReactNode} [props.endAdornment] adorno al final (en contraseñas manda el ojo)\n * @param {boolean} [props.uppercase] normaliza a mayúsculas mientras se escribe\n * @param {(value: string) => string} [props.normalizeOnBlur]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef] para enfocar el campo (react-hook-form lo usa)\n * @param {boolean} [props.clearable] la ⓧ para borrar el texto; por omisión sí (no en contraseñas ni con adorno propio)\n * @param {boolean} [props.multiline] (y lo demás de TextField de MUI: minRows, autoComplete, autoFocus, slotProps, sx…)\n * @param {number} [props.minRows]\n * @param {number} [props.maxRows]\n * @param {string} [props.autoComplete]\n * @param {boolean} [props.autoFocus]\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.required]\n * @param {boolean} [props.fullWidth]\n * @param {string} [props.id]\n * @param {object} [props.slotProps]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function TextInput({\n name, label, value, onChange, onBlur, error, type = 'text', helperText = ' ', startAdornment, endAdornment: customEndAdornment,\n uppercase = false, normalizeOnBlur, inputRef, clearable = true, texts, ...textFieldProps\n}) {\n const {t} = useToolkitTranslation('common', texts);\n const [isRevealed, setIsRevealed] = useState(false);\n const generatedId = useId();\n const inputId = textFieldProps.id ?? `${name ?? 'text'}-${generatedId}`;\n const errorText = translateValidationMessage(t, error);\n const isPassword = type === 'password';\n const revealLabel = isRevealed ? t('form.hidePassword') : t('form.showPassword');\n\n const passwordAdornment = isPassword ? (\n <InputAdornment position=\"end\">\n <Tooltip title={revealLabel}>\n <IconButton size=\"small\" edge=\"end\" aria-label={revealLabel} onClick={() => setIsRevealed((currentValue) => !currentValue)}>\n {isRevealed ? <VisibilityOffOutlinedIcon fontSize=\"small\" /> : <VisibilityOutlinedIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n </InputAdornment>\n ) : undefined;\n const isEditable = !textFieldProps.disabled && !textFieldProps.slotProps?.htmlInput?.readOnly;\n const canClear = clearable && !isPassword && !customEndAdornment && isEditable && Boolean(value);\n // onMouseDown sin acción: el foco se queda en el campo al borrar (si no, el campo pierde el foco y se valida de más)\n const clearAdornment = canClear ? (\n <InputAdornment position=\"end\">\n <IconButton size=\"small\" edge=\"end\" tabIndex={-1} aria-label={t('form.clear')} className={CLEAR_BUTTON_CLASS}\n onMouseDown={(event) => event.preventDefault()} onClick={() => onChange('')}>\n <CancelIcon fontSize=\"small\" />\n </IconButton>\n </InputAdornment>\n ) : undefined;\n const endAdornment = customEndAdornment ? <InputAdornment position=\"end\">{customEndAdornment}</InputAdornment> : (passwordAdornment ?? clearAdornment);\n\n const handleBlur = () => {\n if (normalizeOnBlur) {\n const normalizedValue = normalizeOnBlur(value ?? '');\n // solo si cambió: un cambio de más dispara una validación de más\n if (normalizedValue !== value) {\n onChange(normalizedValue);\n }\n }\n onBlur?.();\n };\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <TextField\n {...textFieldProps}\n name={name}\n id={inputId}\n inputRef={inputRef}\n value={value ?? ''}\n onChange={(event) => onChange(uppercase ? event.target.value.toUpperCase() : event.target.value)}\n onBlur={handleBlur}\n type={isPassword && isRevealed ? 'text' : type}\n sx={[CLEAR_BUTTON_SX, ...[].concat(textFieldProps.sx ?? [])]}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n fullWidth\n size=\"small\"\n slotProps={{\n input: {\n startAdornment: startAdornment ? <InputAdornment position=\"start\">{startAdornment}</InputAdornment> : undefined,\n endAdornment,\n },\n }}\n />\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TextInput.js","sources":["../../src/inputs/TextInput.jsx"],"sourcesContent":["// Campo de texto suelto, con la etiqueta arriba: el que pintan FormTextField (formik) y el de react-hook-form, y el que se\n// usa sin formulario (controlado: value / onChange).\n//\n// Lo que resuelve, y por lo que existe en vez de un <TextField> suelto:\n// - el renglón de ayuda está reservado desde el principio, así la tarjeta no brinca cuando aparece un error;\n// - las contraseñas traen el ojo para revelarlas (con aria-label traducido);\n// - con texto, la ⓧ para borrarlo de un toque, como los campos de Apple («Display a Clear button in the trailing end of\n// a text field», HIG «Text fields»). Sale con el cursor encima o al escribir, para que un formulario lleno no se vea\n// lleno de ⓧ; no entra al recorrido del teclado (con el teclado se borra seleccionando el texto);\n// - normalizeOnBlur deja el valor como lo va a mandar el formulario (el correo en minúsculas y sin espacios) al salir del\n// campo: la persona ve lo que se envía;\n// - uppercase normaliza a mayúsculas mientras se escribe (claves, RFC);\n// - el error llega como LLAVE de yup ({key, values}) o como texto ya traducido (el del servidor) y se pinta en el idioma\n// activo. Cuándo enseñarlo (ya se tocó, ya se envió) lo decide quien lo conecta al formulario.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTextInput con defaultProps y\n// styleOverrides.field (el TextField). El sx de la pantalla sigue ganando a todo.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport CancelIcon from '@mui/icons-material/Cancel';\nimport VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined';\nimport VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y su parte (theme.components.CalcitaTextInput): el campo de MUI con lo de Calcita abajo,\n// el tema del proyecto encima y el sx de la pantalla al final\nconst THEME_NAME = 'CalcitaTextInput';\nconst InputField = piecePart(THEME_NAME, 'Field', TextField);\n\n// la ⓧ de borrar: escondida hasta que el campo tiene el cursor encima o se está escribiendo en él\nconst CLEAR_BUTTON_CLASS = 'calcita-clear-button';\nconst CLEAR_BUTTON_SX = Object.freeze({\n [`& .${CLEAR_BUTTON_CLASS}`]: {visibility: 'hidden', color: 'text.secondary'},\n [`& .MuiInputBase-root:hover .${CLEAR_BUTTON_CLASS}, & .MuiInputBase-root.Mui-focused .${CLEAR_BUTTON_CLASS}`]: {visibility: 'visible'},\n // un campo de búsqueda (type=\"search\") trae la × del navegador: con la ⓧ de la casa salían dos para lo mismo (lo cachó\n // una captura de Tareas). Se esconde la del navegador; la de la casa es la que se ve igual en todos\n '& input[type=\"search\"]::-webkit-search-cancel-button': {WebkitAppearance: 'none', display: 'none'},\n});\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value\n * @param {(value: string) => void} props.onChange recibe el texto, no el evento\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error] el error a enseñar (undefined = sin error)\n * @param {string} [props.type] 'password' agrega el ojo\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] ya traducido; el ejemplo de qué escribir (se va al escribir)\n * @param {import('react').ReactNode} [props.startAdornment]\n * @param {import('react').ReactNode} [props.endAdornment] adorno al final (en contraseñas manda el ojo)\n * @param {boolean} [props.uppercase] normaliza a mayúsculas mientras se escribe\n * @param {(value: string) => string} [props.normalizeOnBlur]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef] para enfocar el campo (react-hook-form lo usa)\n * @param {boolean} [props.clearable] la ⓧ para borrar el texto; por omisión sí (no en contraseñas ni con adorno propio)\n * @param {boolean} [props.multiline] (y lo demás de TextField de MUI: minRows, autoComplete, autoFocus, slotProps, sx…)\n * @param {number} [props.minRows]\n * @param {number} [props.maxRows]\n * @param {string} [props.autoComplete]\n * @param {boolean} [props.autoFocus]\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.required]\n * @param {boolean} [props.fullWidth]\n * @param {string} [props.id]\n * @param {object} [props.slotProps]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function TextInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaTextInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, error, type = 'text', helperText = ' ', startAdornment, endAdornment: customEndAdornment,\n uppercase = false, normalizeOnBlur, inputRef, clearable = true, texts, ...textFieldProps\n } = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const [isRevealed, setIsRevealed] = useState(false);\n const generatedId = useId();\n const inputId = textFieldProps.id ?? `${name ?? 'text'}-${generatedId}`;\n const errorText = translateValidationMessage(t, error);\n const isPassword = type === 'password';\n const revealLabel = isRevealed ? t('form.hidePassword') : t('form.showPassword');\n\n const passwordAdornment = isPassword ? (\n <InputAdornment position=\"end\">\n <Tooltip title={revealLabel}>\n <IconButton size=\"small\" edge=\"end\" aria-label={revealLabel} onClick={() => setIsRevealed((currentValue) => !currentValue)}>\n {isRevealed ? <VisibilityOffOutlinedIcon fontSize=\"small\" /> : <VisibilityOutlinedIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n </InputAdornment>\n ) : undefined;\n const isEditable = !textFieldProps.disabled && !textFieldProps.slotProps?.htmlInput?.readOnly;\n const canClear = clearable && !isPassword && !customEndAdornment && isEditable && Boolean(value);\n // onMouseDown sin acción: el foco se queda en el campo al borrar (si no, el campo pierde el foco y se valida de más)\n const clearAdornment = canClear ? (\n <InputAdornment position=\"end\">\n <IconButton size=\"small\" edge=\"end\" tabIndex={-1} aria-label={t('form.clear')} className={CLEAR_BUTTON_CLASS}\n onMouseDown={(event) => event.preventDefault()} onClick={() => onChange('')}>\n <CancelIcon fontSize=\"small\" />\n </IconButton>\n </InputAdornment>\n ) : undefined;\n const endAdornment = customEndAdornment ? <InputAdornment position=\"end\">{customEndAdornment}</InputAdornment> : (passwordAdornment ?? clearAdornment);\n\n const handleBlur = () => {\n if (normalizeOnBlur) {\n const normalizedValue = normalizeOnBlur(value ?? '');\n // solo si cambió: un cambio de más dispara una validación de más\n if (normalizedValue !== value) {\n onChange(normalizedValue);\n }\n }\n onBlur?.();\n };\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <InputField\n {...textFieldProps}\n name={name}\n id={inputId}\n inputRef={inputRef}\n value={value ?? ''}\n onChange={(event) => onChange(uppercase ? event.target.value.toUpperCase() : event.target.value)}\n onBlur={handleBlur}\n type={isPassword && isRevealed ? 'text' : type}\n ownSx={CLEAR_BUTTON_SX}\n sx={textFieldProps.sx}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n fullWidth\n size=\"small\"\n slotProps={{\n input: {\n startAdornment: startAdornment ? <InputAdornment position=\"start\">{startAdornment}</InputAdornment> : undefined,\n endAdornment,\n },\n }}\n />\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -8,23 +8,29 @@ import Step from "@mui/material/Step";
8
8
  import StepLabel from "@mui/material/StepLabel";
9
9
  import Stepper from "@mui/material/Stepper";
10
10
  import Typography from "@mui/material/Typography";
11
- function WizardLayout({
12
- steps,
13
- activeIndex,
14
- skippedKeys,
15
- isSubmitting = false,
16
- onBack,
17
- onNext,
18
- onSkip,
19
- submitButton,
20
- problemAlert,
21
- texts
22
- }) {
11
+ import { piecePart, usePieceProps } from "../theme/themePieces.js";
12
+ const THEME_NAME = "CalcitaWizardLayout";
13
+ const WizardStepper = piecePart(THEME_NAME, "Stepper", Stepper);
14
+ const WizardPhoneSteps = piecePart(THEME_NAME, "PhoneSteps", Box);
15
+ const WizardActions = piecePart(THEME_NAME, "Actions", Stack);
16
+ function WizardLayout(props) {
17
+ const {
18
+ steps,
19
+ activeIndex,
20
+ skippedKeys,
21
+ isSubmitting = false,
22
+ onBack,
23
+ onNext,
24
+ onSkip,
25
+ submitButton,
26
+ problemAlert,
27
+ texts
28
+ } = usePieceProps(props, THEME_NAME);
23
29
  const { t } = useToolkitTranslation("common", texts);
24
30
  const activeStep = steps[activeIndex];
25
31
  const isLastStep = activeIndex === steps.length - 1;
26
32
  return /* @__PURE__ */ jsxs(Stack, { spacing: 3, children: [
27
- /* @__PURE__ */ jsx(Stepper, { activeStep: activeIndex, alternativeLabel: true, sx: { display: { xs: "none", sm: "flex" } }, children: steps.map((step) => {
33
+ /* @__PURE__ */ jsx(WizardStepper, { activeStep: activeIndex, alternativeLabel: true, ownSx: { display: { xs: "none", sm: "flex" } }, children: steps.map((step) => {
28
34
  let caption;
29
35
  if (skippedKeys.has(step.key)) {
30
36
  caption = t("wizard.skipped");
@@ -33,13 +39,13 @@ function WizardLayout({
33
39
  }
34
40
  return /* @__PURE__ */ jsx(Step, { completed: skippedKeys.has(step.key) ? false : void 0, children: /* @__PURE__ */ jsx(StepLabel, { optional: caption && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: caption }), children: step.label }) }, step.key);
35
41
  }) }),
36
- /* @__PURE__ */ jsxs(Box, { sx: { display: { xs: "block", sm: "none" } }, children: [
42
+ /* @__PURE__ */ jsxs(WizardPhoneSteps, { ownSx: { display: { xs: "block", sm: "none" } }, children: [
37
43
  /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "text.secondary", children: t("wizard.stepOf", { current: activeIndex + 1, total: steps.length }) }),
38
44
  /* @__PURE__ */ jsx(Typography, { variant: "h6", children: activeStep.label })
39
45
  ] }),
40
46
  /* @__PURE__ */ jsx(Box, { children: activeStep.content }),
41
47
  isLastStep && problemAlert,
42
- /* @__PURE__ */ jsxs(Stack, { direction: { xs: "column-reverse", sm: "row" }, spacing: 1.5, sx: { justifyContent: "space-between" }, children: [
48
+ /* @__PURE__ */ jsxs(WizardActions, { direction: { xs: "column-reverse", sm: "row" }, spacing: 1.5, ownSx: { justifyContent: "space-between" }, children: [
43
49
  /* @__PURE__ */ jsx(Button, { variant: "text", color: "inherit", disabled: activeIndex === 0 || isSubmitting, onClick: onBack, children: t("wizard.back") }),
44
50
  /* @__PURE__ */ jsxs(Stack, { direction: { xs: "column-reverse", sm: "row" }, spacing: 1.5, children: [
45
51
  activeStep.optional && !isLastStep && /* @__PURE__ */ jsx(Button, { variant: "text", onClick: onSkip, children: t("wizard.skip") }),
@@ -1 +1 @@
1
- {"version":3,"file":"WizardLayout.js","sources":["../../src/inputs/WizardLayout.jsx"],"sourcesContent":["// Lo que se ve de un formulario por pasos: los pasos arriba (en el teléfono, «Paso 2 de 4» y su nombre), los campos del paso\n// y los botones (Anterior · Omitir este paso · Siguiente o Enviar). No sabe de formik ni de react-hook-form: el FormWizard de\n// cada uno (/forms y /react-hook-form) pone la lógica (qué revisa «Siguiente», qué hace «Omitir») y le pasa su botón de\n// enviar y su aviso de errores. Suelto sirve para armar un asistente propio con otra librería.\n//\n// Los textos se cambian con `texts`, con la forma de `common` en los textos del paquete: {wizard: {next: 'Continuar'}}.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Step from '@mui/material/Step';\nimport StepLabel from '@mui/material/StepLabel';\nimport Stepper from '@mui/material/Stepper';\nimport Typography from '@mui/material/Typography';\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {number} props.activeIndex el paso que se ve\n * @param {Set<string>} props.skippedKeys los pasos omitidos\n * @param {boolean} [props.isSubmitting] mientras se envía, «Anterior» se apaga\n * @param {() => void} props.onBack\n * @param {() => void} props.onNext\n * @param {() => void} props.onSkip\n * @param {import('react').ReactNode} props.submitButton el botón de enviar del último paso\n * @param {import('react').ReactNode} [props.problemAlert] el aviso de errores del servidor, arriba de los botones del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function WizardLayout({\n steps, activeIndex, skippedKeys, isSubmitting = false, onBack, onNext, onSkip, submitButton, problemAlert, texts,\n}) {\n const {t} = useToolkitTranslation('common', texts);\n const activeStep = steps[activeIndex];\n const isLastStep = activeIndex === steps.length - 1;\n\n return (\n <Stack spacing={3}>\n {/* en el teléfono los nombres de los pasos no caben: se dice en qué paso va */}\n <Stepper activeStep={activeIndex} alternativeLabel sx={{display: {xs: 'none', sm: 'flex'}}}>\n {steps.map((step) => {\n let caption;\n if (skippedKeys.has(step.key)) {\n caption = t('wizard.skipped');\n } else if (step.optional) {\n caption = t('wizard.optional');\n }\n return (\n <Step key={step.key} completed={skippedKeys.has(step.key) ? false : undefined}>\n <StepLabel optional={caption && <Typography variant=\"caption\" color=\"text.secondary\">{caption}</Typography>}>\n {step.label}\n </StepLabel>\n </Step>\n );\n })}\n </Stepper>\n <Box sx={{display: {xs: 'block', sm: 'none'}}}>\n <Typography variant=\"overline\" color=\"text.secondary\">{t('wizard.stepOf', {current: activeIndex + 1, total: steps.length})}</Typography>\n <Typography variant=\"h6\">{activeStep.label}</Typography>\n </Box>\n\n <Box>{activeStep.content}</Box>\n\n {isLastStep && problemAlert}\n\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5} sx={{justifyContent: 'space-between'}}>\n <Button variant=\"text\" color=\"inherit\" disabled={activeIndex === 0 || isSubmitting} onClick={onBack}>\n {t('wizard.back')}\n </Button>\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5}>\n {activeStep.optional && !isLastStep && (\n <Button variant=\"text\" onClick={onSkip}>{t('wizard.skip')}</Button>\n )}\n {isLastStep\n ? submitButton\n : <Button variant=\"contained\" size=\"large\" onClick={onNext}>{t('wizard.next')}</Button>}\n </Stack>\n </Stack>\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAkCA,SAAwB,aAAa;AAAA,EACjC;AAAA,EAAO;AAAA,EAAa;AAAA,EAAa,eAAe;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAc;AAAA,EAAc;AAC/G,GAAG;AACC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,aAAa,MAAM,WAAW;AACpC,QAAM,aAAa,gBAAgB,MAAM,SAAS;AAElD,SACI,qBAAC,OAAA,EAAM,SAAS,GAEZ,UAAA;AAAA,IAAA,oBAAC,WAAQ,YAAY,aAAa,kBAAgB,MAAC,IAAI,EAAC,SAAS,EAAC,IAAI,QAAQ,IAAI,OAAA,EAAM,GACnF,UAAA,MAAM,IAAI,CAAC,SAAS;AACjB,UAAI;AACJ,UAAI,YAAY,IAAI,KAAK,GAAG,GAAG;AAC3B,kBAAU,EAAE,gBAAgB;AAAA,MAChC,WAAW,KAAK,UAAU;AACtB,kBAAU,EAAE,iBAAiB;AAAA,MACjC;AACA,aACI,oBAAC,MAAA,EAAoB,WAAW,YAAY,IAAI,KAAK,GAAG,IAAI,QAAQ,QAChE,UAAA,oBAAC,WAAA,EAAU,UAAU,WAAW,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAkB,UAAA,QAAA,CAAQ,GACzF,UAAA,KAAK,MAAA,CACV,EAAA,GAHO,KAAK,GAIhB;AAAA,IAER,CAAC,EAAA,CACL;AAAA,IACA,qBAAC,KAAA,EAAI,IAAI,EAAC,SAAS,EAAC,IAAI,SAAS,IAAI,OAAA,EAAM,GACvC,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,YAAW,OAAM,kBAAkB,UAAA,EAAE,iBAAiB,EAAC,SAAS,cAAc,GAAG,OAAO,MAAM,OAAA,CAAO,GAAE;AAAA,MAC3H,oBAAC,YAAA,EAAW,SAAQ,MAAM,qBAAW,MAAA,CAAM;AAAA,IAAA,GAC/C;AAAA,IAEA,oBAAC,KAAA,EAAK,UAAA,WAAW,QAAA,CAAQ;AAAA,IAExB,cAAc;AAAA,IAEf,qBAAC,OAAA,EAAM,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KAAK,IAAI,EAAC,gBAAgB,mBACpF,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAO,SAAQ,QAAO,OAAM,WAAU,UAAU,gBAAgB,KAAK,cAAc,SAAS,QACxF,UAAA,EAAE,aAAa,GACpB;AAAA,MACA,qBAAC,OAAA,EAAM,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KACzD,UAAA;AAAA,QAAA,WAAW,YAAY,CAAC,cACrB,oBAAC,QAAA,EAAO,SAAQ,QAAO,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,QAE7D,aACK,eACA,oBAAC,QAAA,EAAO,SAAQ,aAAY,MAAK,SAAQ,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,MAAA,EAAA,CACtF;AAAA,IAAA,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"WizardLayout.js","sources":["../../src/inputs/WizardLayout.jsx"],"sourcesContent":["// Lo que se ve de un formulario por pasos: los pasos arriba (en el teléfono, «Paso 2 de 4» y su nombre), los campos del paso\n// y los botones (Anterior · Omitir este paso · Siguiente o Enviar). No sabe de formik ni de react-hook-form: el FormWizard de\n// cada uno (/forms y /react-hook-form) pone la lógica (qué revisa «Siguiente», qué hace «Omitir») y le pasa su botón de\n// enviar y su aviso de errores. Suelto sirve para armar un asistente propio con otra librería.\n//\n// Los textos se cambian con `texts`, con la forma de `common` en los textos del paquete: {wizard: {next: 'Continuar'}}.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWizardLayout con defaultProps y\n// styleOverrides de sus partes: stepper, phoneSteps, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Step from '@mui/material/Step';\nimport StepLabel from '@mui/material/StepLabel';\nimport Stepper from '@mui/material/Stepper';\nimport Typography from '@mui/material/Typography';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaWizardLayout)\nconst THEME_NAME = 'CalcitaWizardLayout';\nconst WizardStepper = piecePart(THEME_NAME, 'Stepper', Stepper);\nconst WizardPhoneSteps = piecePart(THEME_NAME, 'PhoneSteps', Box);\nconst WizardActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {number} props.activeIndex el paso que se ve\n * @param {Set<string>} props.skippedKeys los pasos omitidos\n * @param {boolean} [props.isSubmitting] mientras se envía, «Anterior» se apaga\n * @param {() => void} props.onBack\n * @param {() => void} props.onNext\n * @param {() => void} props.onSkip\n * @param {import('react').ReactNode} props.submitButton el botón de enviar del último paso\n * @param {import('react').ReactNode} [props.problemAlert] el aviso de errores del servidor, arriba de los botones del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function WizardLayout(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWizardLayout.defaultProps) y encima lo que llega aquí\n const {\n steps, activeIndex, skippedKeys, isSubmitting = false, onBack, onNext, onSkip, submitButton, problemAlert, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const activeStep = steps[activeIndex];\n const isLastStep = activeIndex === steps.length - 1;\n\n return (\n <Stack spacing={3}>\n {/* en el teléfono los nombres de los pasos no caben: se dice en qué paso va */}\n <WizardStepper activeStep={activeIndex} alternativeLabel ownSx={{display: {xs: 'none', sm: 'flex'}}}>\n {steps.map((step) => {\n let caption;\n if (skippedKeys.has(step.key)) {\n caption = t('wizard.skipped');\n } else if (step.optional) {\n caption = t('wizard.optional');\n }\n return (\n <Step key={step.key} completed={skippedKeys.has(step.key) ? false : undefined}>\n <StepLabel optional={caption && <Typography variant=\"caption\" color=\"text.secondary\">{caption}</Typography>}>\n {step.label}\n </StepLabel>\n </Step>\n );\n })}\n </WizardStepper>\n <WizardPhoneSteps ownSx={{display: {xs: 'block', sm: 'none'}}}>\n <Typography variant=\"overline\" color=\"text.secondary\">{t('wizard.stepOf', {current: activeIndex + 1, total: steps.length})}</Typography>\n <Typography variant=\"h6\">{activeStep.label}</Typography>\n </WizardPhoneSteps>\n\n <Box>{activeStep.content}</Box>\n\n {isLastStep && problemAlert}\n\n <WizardActions direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5} ownSx={{justifyContent: 'space-between'}}>\n <Button variant=\"text\" color=\"inherit\" disabled={activeIndex === 0 || isSubmitting} onClick={onBack}>\n {t('wizard.back')}\n </Button>\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5}>\n {activeStep.optional && !isLastStep && (\n <Button variant=\"text\" onClick={onSkip}>{t('wizard.skip')}</Button>\n )}\n {isLastStep\n ? submitButton\n : <Button variant=\"contained\" size=\"large\" onClick={onNext}>{t('wizard.next')}</Button>}\n </Stack>\n </WizardActions>\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAsBA,MAAM,aAAa;AACnB,MAAM,gBAAgB,UAAU,YAAY,WAAW,OAAO;AAC9D,MAAM,mBAAmB,UAAU,YAAY,cAAc,GAAG;AAChE,MAAM,gBAAgB,UAAU,YAAY,WAAW,KAAK;AAmB5D,SAAwB,aAAa,OAAO;AAExC,QAAM;AAAA,IACN;AAAA,IAAO;AAAA,IAAa;AAAA,IAAa,eAAe;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAc;AAAA,IAAc;AAAA,EAAA,IAC3G,cAAc,OAAO,UAAU;AAC/B,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,aAAa,MAAM,WAAW;AACpC,QAAM,aAAa,gBAAgB,MAAM,SAAS;AAElD,SACI,qBAAC,OAAA,EAAM,SAAS,GAEZ,UAAA;AAAA,IAAA,oBAAC,iBAAc,YAAY,aAAa,kBAAgB,MAAC,OAAO,EAAC,SAAS,EAAC,IAAI,QAAQ,IAAI,OAAA,EAAM,GAC5F,UAAA,MAAM,IAAI,CAAC,SAAS;AACjB,UAAI;AACJ,UAAI,YAAY,IAAI,KAAK,GAAG,GAAG;AAC3B,kBAAU,EAAE,gBAAgB;AAAA,MAChC,WAAW,KAAK,UAAU;AACtB,kBAAU,EAAE,iBAAiB;AAAA,MACjC;AACA,aACI,oBAAC,MAAA,EAAoB,WAAW,YAAY,IAAI,KAAK,GAAG,IAAI,QAAQ,QAChE,UAAA,oBAAC,WAAA,EAAU,UAAU,WAAW,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAkB,UAAA,QAAA,CAAQ,GACzF,UAAA,KAAK,MAAA,CACV,EAAA,GAHO,KAAK,GAIhB;AAAA,IAER,CAAC,EAAA,CACL;AAAA,IACA,qBAAC,kBAAA,EAAiB,OAAO,EAAC,SAAS,EAAC,IAAI,SAAS,IAAI,OAAA,EAAM,GACvD,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,YAAW,OAAM,kBAAkB,UAAA,EAAE,iBAAiB,EAAC,SAAS,cAAc,GAAG,OAAO,MAAM,OAAA,CAAO,GAAE;AAAA,MAC3H,oBAAC,YAAA,EAAW,SAAQ,MAAM,qBAAW,MAAA,CAAM;AAAA,IAAA,GAC/C;AAAA,IAEA,oBAAC,KAAA,EAAK,UAAA,WAAW,QAAA,CAAQ;AAAA,IAExB,cAAc;AAAA,IAEf,qBAAC,eAAA,EAAc,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KAAK,OAAO,EAAC,gBAAgB,mBAC/F,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAO,SAAQ,QAAO,OAAM,WAAU,UAAU,gBAAgB,KAAK,cAAc,SAAS,QACxF,UAAA,EAAE,aAAa,GACpB;AAAA,MACA,qBAAC,OAAA,EAAM,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KACzD,UAAA;AAAA,QAAA,WAAW,YAAY,CAAC,cACrB,oBAAC,QAAA,EAAO,SAAQ,QAAO,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,QAE7D,aACK,eACA,oBAAC,QAAA,EAAO,SAAQ,aAAY,MAAK,SAAQ,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,MAAA,EAAA,CACtF;AAAA,IAAA,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;"}