@stackcraftmx/calcita 0.51.0 → 0.53.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 (214) hide show
  1. package/README.md +73 -6
  2. package/cli/calcita.js +4 -2
  3. package/cli/checkTexts.js +66 -9
  4. package/dist/appShell/BarActionButton.js.map +1 -1
  5. package/dist/datePickers/DateInput.js +5 -3
  6. package/dist/datePickers/DateInput.js.map +1 -1
  7. package/dist/datePickers/DateTimeInput.js +6 -4
  8. package/dist/datePickers/DateTimeInput.js.map +1 -1
  9. package/dist/datePickers/FormDateField.js +1 -1
  10. package/dist/datePickers/FormDateField.js.map +1 -1
  11. package/dist/datePickers/FormDateTimeField.js +1 -1
  12. package/dist/datePickers/FormDateTimeField.js.map +1 -1
  13. package/dist/datePickers/FormMonthField.js +1 -1
  14. package/dist/datePickers/FormMonthField.js.map +1 -1
  15. package/dist/datePickers/FormTimeField.js +1 -1
  16. package/dist/datePickers/FormTimeField.js.map +1 -1
  17. package/dist/datePickers/MonthInput.js +5 -3
  18. package/dist/datePickers/MonthInput.js.map +1 -1
  19. package/dist/datePickers/PickerFrame.js +2 -2
  20. package/dist/datePickers/PickerFrame.js.map +1 -1
  21. package/dist/datePickers/TimeInput.js +6 -4
  22. package/dist/datePickers/TimeInput.js.map +1 -1
  23. package/dist/feedback/Toaster.js +11 -2
  24. package/dist/feedback/Toaster.js.map +1 -1
  25. package/dist/forms/CalcitaFormik.js +11 -0
  26. package/dist/forms/CalcitaFormik.js.map +1 -0
  27. package/dist/forms/FormAttachmentsField.js +1 -1
  28. package/dist/forms/FormAttachmentsField.js.map +1 -1
  29. package/dist/forms/FormCatalogField.js +1 -1
  30. package/dist/forms/FormCatalogField.js.map +1 -1
  31. package/dist/forms/FormCheckboxGroupField.js +1 -1
  32. package/dist/forms/FormCheckboxGroupField.js.map +1 -1
  33. package/dist/forms/FormColorField.js +1 -1
  34. package/dist/forms/FormColorField.js.map +1 -1
  35. package/dist/forms/FormFileField.js +1 -1
  36. package/dist/forms/FormFileField.js.map +1 -1
  37. package/dist/forms/FormFileUploadField.js +1 -1
  38. package/dist/forms/FormFileUploadField.js.map +1 -1
  39. package/dist/forms/FormImageField.js +1 -1
  40. package/dist/forms/FormImageField.js.map +1 -1
  41. package/dist/forms/FormMaskedField.js +1 -1
  42. package/dist/forms/FormMaskedField.js.map +1 -1
  43. package/dist/forms/FormNumberField.js +1 -1
  44. package/dist/forms/FormNumberField.js.map +1 -1
  45. package/dist/forms/FormSchemaProvider.js +17 -0
  46. package/dist/forms/FormSchemaProvider.js.map +1 -0
  47. package/dist/forms/FormSelectField.js +1 -1
  48. package/dist/forms/FormSelectField.js.map +1 -1
  49. package/dist/forms/FormTextField.js +1 -1
  50. package/dist/forms/FormTextField.js.map +1 -1
  51. package/dist/forms/FormTransferListField.js +1 -1
  52. package/dist/forms/FormTransferListField.js.map +1 -1
  53. package/dist/forms/FormWizard.js +23 -6
  54. package/dist/forms/FormWizard.js.map +1 -1
  55. package/dist/forms/schemaRequired.js +33 -0
  56. package/dist/forms/schemaRequired.js.map +1 -0
  57. package/dist/forms/useFormikField.js +18 -5
  58. package/dist/forms/useFormikField.js.map +1 -1
  59. package/dist/forms.js +52 -46
  60. package/dist/forms.js.map +1 -1
  61. package/dist/hookForm/FormAttachmentsField.js +1 -1
  62. package/dist/hookForm/FormAttachmentsField.js.map +1 -1
  63. package/dist/hookForm/FormCatalogField.js +1 -1
  64. package/dist/hookForm/FormCatalogField.js.map +1 -1
  65. package/dist/hookForm/FormCheckboxGroupField.js +1 -1
  66. package/dist/hookForm/FormCheckboxGroupField.js.map +1 -1
  67. package/dist/hookForm/FormColorField.js +1 -1
  68. package/dist/hookForm/FormColorField.js.map +1 -1
  69. package/dist/hookForm/FormFileField.js +1 -1
  70. package/dist/hookForm/FormFileField.js.map +1 -1
  71. package/dist/hookForm/FormFileUploadField.js +1 -1
  72. package/dist/hookForm/FormFileUploadField.js.map +1 -1
  73. package/dist/hookForm/FormImageField.js +1 -1
  74. package/dist/hookForm/FormImageField.js.map +1 -1
  75. package/dist/hookForm/FormMaskedField.js +1 -1
  76. package/dist/hookForm/FormMaskedField.js.map +1 -1
  77. package/dist/hookForm/FormNumberField.js +1 -1
  78. package/dist/hookForm/FormNumberField.js.map +1 -1
  79. package/dist/hookForm/FormSelectField.js +1 -1
  80. package/dist/hookForm/FormSelectField.js.map +1 -1
  81. package/dist/hookForm/FormTextField.js +1 -1
  82. package/dist/hookForm/FormTextField.js.map +1 -1
  83. package/dist/hookForm/FormTransferListField.js +1 -1
  84. package/dist/hookForm/FormTransferListField.js.map +1 -1
  85. package/dist/hookForm/FormWizard.js +21 -5
  86. package/dist/hookForm/FormWizard.js.map +1 -1
  87. package/dist/hookForm/useHookFormField.js +9 -3
  88. package/dist/hookForm/useHookFormField.js.map +1 -1
  89. package/dist/index.js +12 -3
  90. package/dist/index.js.map +1 -1
  91. package/dist/inputs/CatalogInput.js +3 -2
  92. package/dist/inputs/CatalogInput.js.map +1 -1
  93. package/dist/inputs/CheckboxGroupInput.js +2 -1
  94. package/dist/inputs/CheckboxGroupInput.js.map +1 -1
  95. package/dist/inputs/ColorInput.js +3 -1
  96. package/dist/inputs/ColorInput.js.map +1 -1
  97. package/dist/inputs/FieldFrame.js +18 -3
  98. package/dist/inputs/FieldFrame.js.map +1 -1
  99. package/dist/inputs/FormSection.js +42 -11
  100. package/dist/inputs/FormSection.js.map +1 -1
  101. package/dist/inputs/NumberInput.js +3 -1
  102. package/dist/inputs/NumberInput.js.map +1 -1
  103. package/dist/inputs/SelectInput.js +3 -1
  104. package/dist/inputs/SelectInput.js.map +1 -1
  105. package/dist/inputs/TextInput.js +1 -1
  106. package/dist/inputs/TextInput.js.map +1 -1
  107. package/dist/inputs/TransferListInput.js +2 -1
  108. package/dist/inputs/TransferListInput.js.map +1 -1
  109. package/dist/inputs/WizardLayout.js +51 -17
  110. package/dist/inputs/WizardLayout.js.map +1 -1
  111. package/dist/inputs/formLayoutContext.js +7 -2
  112. package/dist/inputs/formLayoutContext.js.map +1 -1
  113. package/dist/inputs/requiredMark.js +17 -0
  114. package/dist/inputs/requiredMark.js.map +1 -0
  115. package/dist/inputs/useWizardSteps.js +36 -0
  116. package/dist/inputs/useWizardSteps.js.map +1 -0
  117. package/dist/inputs/wizardSteps.js +32 -0
  118. package/dist/inputs/wizardSteps.js.map +1 -0
  119. package/dist/locales/en.js +1 -0
  120. package/dist/locales/en.js.map +1 -1
  121. package/dist/locales/es.js +1 -0
  122. package/dist/locales/es.js.map +1 -1
  123. package/dist/richText/FormRichTextField.js +1 -1
  124. package/dist/richText/FormRichTextField.js.map +1 -1
  125. package/dist/richText/RichTextEditor.js +4 -3
  126. package/dist/richText/RichTextEditor.js.map +1 -1
  127. package/dist/toolkit/ToolkitProvider.js +3 -0
  128. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  129. package/dist/toolkit/toolkitContext.js.map +1 -1
  130. package/dist/types/appShell/BarActionButton.d.ts +1 -0
  131. package/dist/types/datePickers/DateInput.d.ts +2 -0
  132. package/dist/types/datePickers/DateTimeInput.d.ts +2 -0
  133. package/dist/types/datePickers/FormDateField.d.ts +3 -3
  134. package/dist/types/datePickers/FormDateTimeField.d.ts +3 -3
  135. package/dist/types/datePickers/FormMonthField.d.ts +3 -3
  136. package/dist/types/datePickers/FormTimeField.d.ts +3 -3
  137. package/dist/types/datePickers/MonthInput.d.ts +2 -0
  138. package/dist/types/datePickers/PickerFrame.d.ts +3 -1
  139. package/dist/types/datePickers/TimeInput.d.ts +2 -0
  140. package/dist/types/feedback/Toaster.d.ts +15 -1
  141. package/dist/types/feedback/index.d.ts +1 -1
  142. package/dist/types/forms/CalcitaFormik.d.ts +6 -0
  143. package/dist/types/forms/FormAttachmentsField.d.ts +3 -3
  144. package/dist/types/forms/FormCatalogField.d.ts +2 -2
  145. package/dist/types/forms/FormCheckboxGroupField.d.ts +3 -3
  146. package/dist/types/forms/FormColorField.d.ts +2 -2
  147. package/dist/types/forms/FormFileField.d.ts +3 -3
  148. package/dist/types/forms/FormFileUploadField.d.ts +3 -3
  149. package/dist/types/forms/FormImageField.d.ts +3 -3
  150. package/dist/types/forms/FormMaskedField.d.ts +2 -2
  151. package/dist/types/forms/FormNumberField.d.ts +2 -2
  152. package/dist/types/forms/FormSchemaProvider.d.ts +13 -0
  153. package/dist/types/forms/FormSelectField.d.ts +2 -2
  154. package/dist/types/forms/FormTextField.d.ts +2 -2
  155. package/dist/types/forms/FormTransferListField.d.ts +3 -3
  156. package/dist/types/forms/FormWizard.d.ts +14 -1
  157. package/dist/types/forms/index.d.ts +1 -0
  158. package/dist/types/forms/schemaRequired.d.ts +14 -0
  159. package/dist/types/forms/useFormikField.d.ts +10 -4
  160. package/dist/types/forms.d.ts +4 -2
  161. package/dist/types/hookForm/FormAttachmentsField.d.ts +3 -3
  162. package/dist/types/hookForm/FormCatalogField.d.ts +3 -3
  163. package/dist/types/hookForm/FormCheckboxGroupField.d.ts +3 -3
  164. package/dist/types/hookForm/FormColorField.d.ts +3 -3
  165. package/dist/types/hookForm/FormFileField.d.ts +3 -3
  166. package/dist/types/hookForm/FormFileUploadField.d.ts +3 -3
  167. package/dist/types/hookForm/FormImageField.d.ts +3 -3
  168. package/dist/types/hookForm/FormMaskedField.d.ts +3 -3
  169. package/dist/types/hookForm/FormNumberField.d.ts +3 -3
  170. package/dist/types/hookForm/FormSelectField.d.ts +3 -3
  171. package/dist/types/hookForm/FormTextField.d.ts +3 -3
  172. package/dist/types/hookForm/FormTransferListField.d.ts +3 -3
  173. package/dist/types/hookForm/FormWizard.d.ts +14 -1
  174. package/dist/types/hookForm/useHookFormField.d.ts +8 -4
  175. package/dist/types/index.d.ts +4 -3
  176. package/dist/types/inputs/CatalogInput.d.ts +1 -0
  177. package/dist/types/inputs/CheckboxGroupInput.d.ts +1 -0
  178. package/dist/types/inputs/ColorInput.d.ts +1 -0
  179. package/dist/types/inputs/FieldFrame.d.ts +1 -0
  180. package/dist/types/inputs/FormSection.d.ts +13 -0
  181. package/dist/types/inputs/NumberInput.d.ts +1 -0
  182. package/dist/types/inputs/SelectInput.d.ts +1 -0
  183. package/dist/types/inputs/TextInput.d.ts +1 -1
  184. package/dist/types/inputs/TransferListInput.d.ts +1 -0
  185. package/dist/types/inputs/WizardLayout.d.ts +6 -2
  186. package/dist/types/inputs/formLayoutContext.d.ts +8 -1
  187. package/dist/types/inputs/requiredMark.d.ts +9 -0
  188. package/dist/types/inputs/useWizardSteps.d.ts +23 -0
  189. package/dist/types/inputs/wizardSteps.d.ts +65 -0
  190. package/dist/types/locales/en.d.ts +1 -0
  191. package/dist/types/locales/es.d.ts +1 -0
  192. package/dist/types/richText/FormRichTextField.d.ts +3 -3
  193. package/dist/types/richText/RichTextEditor.d.ts +1 -0
  194. package/dist/types/toolkit/ToolkitProvider.d.ts +5 -1
  195. package/dist/types/toolkit/toolkitContext.d.ts +2 -1
  196. package/dist/types/ui/cards/RecordCard.d.ts +12 -0
  197. package/dist/types/ui/index.d.ts +1 -1
  198. package/dist/types/ui/navigation/BackButton.d.ts +1 -0
  199. package/dist/types/upload/AttachmentsInput.d.ts +2 -0
  200. package/dist/types/upload/FileInput.d.ts +1 -0
  201. package/dist/types/upload/FileUploadInput.d.ts +1 -0
  202. package/dist/types/upload/ImageInput.d.ts +1 -0
  203. package/dist/ui/cards/RecordCard.js +6 -2
  204. package/dist/ui/cards/RecordCard.js.map +1 -1
  205. package/dist/ui/navigation/BackButton.js.map +1 -1
  206. package/dist/upload/AttachmentsInput.js +2 -1
  207. package/dist/upload/AttachmentsInput.js.map +1 -1
  208. package/dist/upload/FileInput.js +2 -1
  209. package/dist/upload/FileInput.js.map +1 -1
  210. package/dist/upload/FileUploadInput.js +2 -1
  211. package/dist/upload/FileUploadInput.js.map +1 -1
  212. package/dist/upload/ImageInput.js +2 -1
  213. package/dist/upload/ImageInput.js.map +1 -1
  214. package/package.json +1 -1
package/README.md CHANGED
@@ -56,7 +56,10 @@ entrar y las del módulo de ejemplo, y `make:module` arma la de cada módulo nue
56
56
  Y dos revisiones para antes de subir: `pnpm lint` (ESLint: una variable que no existe, «t is not defined», se caza al
57
57
  escribir) y `pnpm check:texts` (`calcita check:texts`: que cada `t('llave')` escrita tal cual exista en todos los idiomas y
58
58
  que los idiomas tengan las mismas llaves). Sirve también en un proyecto que no salió del CLI: lee `src/**/*Texts.js` y
59
- `src/**/texts.js`.
59
+ `src/**/texts.js`; con el código en otra carpeta, `calcita check:texts --source resources/js` (un Laravel). Un archivo de
60
+ textos puede reexportar otro con ruta relativa o con `@/` (aquí apunta a la carpeta del código, como en Vite; pide Node 22.15
61
+ o más). Nunca da un verde falso: sin la carpeta, sin ningún archivo de textos o con uno
62
+ que no se pudo leer, sale con problema y no compara llaves.
60
63
 
61
64
  ## Arrancar en un proyecto que ya existe
62
65
 
@@ -66,7 +69,13 @@ pnpm add formik yup # solo si usa /forms o /account
66
69
  ```
67
70
 
68
71
  Con npm es igual, con `npm install` en lugar de `pnpm add`. Desde la 0.25, formik y yup los pone el proyecto: Calcita usa
69
- la versión que ya tenga, no se casa con una.
72
+ la versión que ya tenga, no se casa con una. Cada entrada aparte pide las suyas (la última columna de la tabla de entradas):
73
+ sin ellas, el import truena al buscar el archivo y no dice qué falta. Por ejemplo, para las fechas,
74
+ `pnpm add @mui/x-date-pickers dayjs`.
75
+
76
+ Al subir de versión, apague y vuelva a prender el servidor de Vite (`pnpm dev` o `npm run dev`): Vite revisa lo instalado
77
+ solo al arrancar, y con el servidor prendido sigue con la copia vieja del paquete y el error se busca en el código propio. No
78
+ hace falta borrar `node_modules/.vite`; si de todos modos quiere que lo rehaga, `vite --force`.
70
79
 
71
80
  ```jsx
72
81
  import {ThemeProvider, createTheme} from '@mui/material/styles';
@@ -86,6 +95,10 @@ export const App = () => (
86
95
 
87
96
  Sin nada más, todo sale en español, con la zona horaria del navegador, montos en pesos y la navegación del navegador.
88
97
 
98
+ `<Toaster />` va una sola vez y acepta lo de sonner tal cual (`duration`, `visibleToasts`, `offset`, `toastOptions`…). Por
99
+ omisión los avisos salen abajo a la izquierda; si ahí estorban (el pie del menú lateral), `position` los cambia de esquina:
100
+ `<Toaster position={TOASTER_POSITION.bottomCenter} />`.
101
+
89
102
  ## Lo que se conecta en ToolkitProvider
90
103
 
91
104
  | Prop | Para qué | Por omisión |
@@ -103,6 +116,7 @@ Sin nada más, todo sale en español, con la zona horaria del navegador, montos
103
116
  | `buildId` | la compilación del front que corre en la pestaña, para el aviso de versión nueva | ninguna (sin aviso) |
104
117
  | `fileTypes` | tipos de archivo propios para las descargas (su ícono y su color), además de Excel, CSV y PDF | los de la casa |
105
118
  | `onMissingText` | a quién avisarle de un texto que no existe (la pantalla enseña la llave): mandarlo a Sentry, tronar una prueba | un aviso en la consola, una vez por idioma y llave |
119
+ | `requiredFromSchema` | los campos de formik leen del esquema de yup qué es obligatorio y ponen su asterisco (ver «Lo obligatorio, a la vista») | no |
106
120
  | `readValidationMessage` | cómo leer los mensajes de validación de un sistema que ya existe (ver «Mensajes de error propios») | `{key}`, `{msg}`, `{message}` y texto |
107
121
 
108
122
  Un `ToolkitProvider` dentro de otro (una parte de la app en otro idioma, una vista previa) toma del de afuera lo que no le
@@ -597,7 +611,8 @@ el servidor del proyecto); `HelpButton` es el «?» que la vuelve a abrir.
597
611
 
598
612
  Para listas donde una tabla se queda corta, `RecordCard`: arriba quién es (avatar con iniciales, folio, nombre) con su
599
613
  estatus, insignias, botón para abrirlo y menú «⋯»; en el cuerpo sus datos en grupos (`groups`, o `content` libre) y el dato
600
- principal grande; abajo lo que va a todo lo ancho y «Ver detalle», que despliega más. El orden es el que se da y nada se
614
+ principal grande; abajo lo que va a todo lo ancho y «Ver detalle», que despliega más (`footerAlign` los junta a la
615
+ izquierda o a la derecha; por omisión «Ver detalle» va en la otra orilla). El orden es el que se da y nada se
601
616
  reacomoda solo: para un orden obligatorio, `trailing` pone el estatus al final y `columns` fija el ancho de cada columna.
602
617
  `RecordCardSkeleton` es su esqueleto. El menú «⋯» (`ActionsMenu`) acepta secciones, separadores, ligas, renglones propios y
603
618
  submenús (`children`), que se abren dentro del mismo menú con «Regresar». Un renglón con `to` es una pantalla de la app: va
@@ -983,8 +998,28 @@ import {Form, Formik, FormNumberField, FormProblemAlert, FormTextField, SubmitBu
983
998
  ```
984
999
 
985
1000
  formik y yup los instala el proyecto (`pnpm add formik yup`); `Formik`, `Form` y `Yup` también se pueden importar de
986
- `@stackcraftmx/calcita/forms`, que es la misma copia. `FormWizard` parte un formulario en pasos, con pasos opcionales (hay
987
- uno para formik y otro para react-hook-form); los ejemplos traen los dos.
1001
+ `@stackcraftmx/calcita/forms`, que es la misma copia (el `Formik` de Calcita es el de formik con el esquema a la vista de los
1002
+ campos, para lo obligatorio). `FormWizard` parte un formulario en pasos, con pasos opcionales (hay uno para formik y otro para
1003
+ react-hook-form); los ejemplos traen los dos. `hideBackOnFirstStep` no pinta «Anterior» en el primer paso (por omisión sale
1004
+ apagado, como antes).
1005
+
1006
+ El `onChange` y el `onBlur` que la pantalla le pase a un campo de formik o de react-hook-form también corren, después de los
1007
+ del formulario y con el valor nuevo: `<FormSelectField name="kind" … onChange={(kind) => loadRatesFor(kind)} />`. Antes los
1008
+ del formulario los pisaban sin avisar.
1009
+
1010
+ Para editar un registro de varios pasos (se entra a corregir un dato suelto, no a recorrer todo), el mismo `FormWizard`:
1011
+
1012
+ ```jsx
1013
+ <FormWizard steps={steps} submitLabel="Guardar cambios"
1014
+ initialStepKey={review.stepKey} // abre en el paso que dice el servidor
1015
+ orientation={WIZARD_ORIENTATION.vertical} // un paso debajo del otro, con sus campos adentro
1016
+ stepSelection={WIZARD_STEP_SELECTION.free} // los títulos se pulsan: salta directo (o .checked: solo sin errores)
1017
+ onStepChange={(stepKey) => setSearchParams({step: stepKey})} />
1018
+ ```
1019
+
1020
+ Con los títulos que se pulsan, la palomita de un paso la pone la pantalla (`steps[].completed`): «antes» ya no quiere decir
1021
+ «hecho». Al enviar, el formulario regresa al primer paso con error, como siempre. Sin estas opciones se porta igual que
1022
+ antes. `WizardLayout` suelto acepta lo mismo (`orientation`, `onStepSelect`); `skippedKeys` y `onSkip` ya no son obligatorios.
988
1023
 
989
1024
  Para que el formulario se vea como los de Apple (Ajustes, la hoja «Nuevo evento» de Calendario), los campos van dentro de
990
1025
  una `FormSection`: cada uno se vuelve un renglón, con la etiqueta a la izquierda y el valor a la derecha, sin la caja de
@@ -1004,6 +1039,35 @@ import {FormRow, FormSection} from '@stackcraftmx/calcita';
1004
1039
  Los campos van directo dentro de la `FormSection` (sin Grid ni Stack en medio): la línea entre renglones se dibuja entre
1005
1040
  hermanos.
1006
1041
 
1042
+ En el teléfono la etiqueta se lleva el 38 % del renglón y un nombre largo se parte en tres renglones: con
1043
+ `labelPlacement={FORM_LABEL_PLACEMENT.topOnPhone}` la etiqueta sube arriba del campo por debajo de `sm`, y en pantallas más
1044
+ anchas sigue a la izquierda. Por omisión `side`, como antes. Para todos los formularios, una vez en el tema:
1045
+ `components: {CalcitaFormSection: {defaultProps: {labelPlacement: 'topOnPhone'}}}`.
1046
+
1047
+ ### Lo obligatorio, a la vista
1048
+
1049
+ Un campo con `required` lleva un asterisco en su etiqueta, en los 17 campos de la casa (texto, número, listas, catálogo,
1050
+ color, casillas, transferencia, máscara, las cuatro fechas, archivos, imagen, adjuntos y texto con formato). El asterisco va
1051
+ en el estilo de la etiqueta y no como texto: la etiqueta sigue diciendo lo mismo (así la siguen encontrando
1052
+ `getByLabel('Nombre', {exact: true})` y `getByLabelText('Nombre')`) y el lector de pantalla no dice «asterisco»; el campo
1053
+ dice que es obligatorio con `required` (como en MUI) o, donde el navegador no sabe revisarlo, con `aria-required`.
1054
+
1055
+ Con formik, el asterisco puede salir del mismo esquema de yup que revisa el formulario, sin marcar cada campo a mano:
1056
+
1057
+ ```jsx
1058
+ <ToolkitProvider requiredFromSchema> {/* una vez, en la raíz */}
1059
+ ```
1060
+
1061
+ Cuenta lo que yup describe como no opcional (`.required()`, `.defined()`) y una lista con `.min(1)`; las condiciones
1062
+ (`.when`) se resuelven con los valores de ese momento. Lo que la pantalla diga con `required` gana. formik no les comparte el
1063
+ esquema a sus campos: el `Formik` de `@stackcraftmx/calcita/forms` lo hace solo; con el de `'formik'`, envuelva los campos en
1064
+ `<FormSchemaProvider schema={schema}>`. Con react-hook-form el esquema queda dentro del resolver: ahí cada campo lleva su
1065
+ `required`. La regla sin React: `isRequiredInSchema(schema, 'contacts.2.email', values)`.
1066
+
1067
+ Como en MUI, `required` también lo revisa el navegador al enviar si el `<Form>` no lleva `noValidate`: póngaselo para que solo
1068
+ revise yup (en el idioma de la app). El asterisco se cambia desde el tema (`label: {'&[data-required]::after': {…}}` de
1069
+ `CalcitaFieldFrame` y `CalcitaFormRow`) o con el texto `common.form.requiredMark`.
1070
+
1007
1071
  ### Mensajes de error propios (sin tocar el esquema)
1008
1072
 
1009
1073
  Los mensajes de `buildYupLocale()` son llaves que se traducen al pintarse, y cada uno lleva **su regla y su campo**. Cuando
@@ -1061,7 +1125,10 @@ idioma de la persona; la pantalla no convierte nada:
1061
1125
  La fecha con hora se ve en la zona del proyecto (`ToolkitProvider timeZone`) con el nombre de la zona abajo («Hora estándar
1062
1126
  central»), para que nadie dude en qué hora está escribiendo; `timeZone` la cambia en un solo campo (la hora de una
1063
1127
  sucursal) y el momento que viaja es el mismo. La hora sale de 24 horas o con a. m. y p. m. según `ToolkitProvider
1064
- hourCycle`, y viaja igual. Lo que el selector de MUI X acepta y aquí no tiene nombre va en `pickerProps`.
1128
+ hourCycle`, y viaja igual. Lo que el selector de MUI X acepta y aquí no tiene nombre va en `pickerProps`; ahí también
1129
+ `format` y `ampm` ganan a los de la casa. `format` también va directo (`<DateInput format="DD/MMM/YYYY" />` enseña
1130
+ «16/jul/2029»; con el mes en letras no hay dudas entre día y mes) y lo que viaja no cambia. Piden
1131
+ `pnpm add @mui/x-date-pickers dayjs`.
1065
1132
 
1066
1133
  ```jsx
1067
1134
  import {FormDateTimeField, FormMonthField, FormTimeField} from '@stackcraftmx/calcita/date-pickers';
package/cli/calcita.js CHANGED
@@ -36,6 +36,7 @@ const HELP = `Calcita: proyectos y esqueletos con la forma de los ejemplos.
36
36
  --module <módulo> … o solo de un módulo
37
37
  calcita check:texts revisa que cada t('llave') exista en todos los idiomas, y que los idiomas tengan
38
38
  las mismas llaves (lo que si no se descubre en la pantalla)
39
+ --source <carpeta> … con el código en otra carpeta (por omisión src; en Laravel, resources/js)
39
40
 
40
41
  Opciones:
41
42
  -y, --yes sin preguntas: lo recomendado, más lo que diga cada opción
@@ -54,6 +55,7 @@ const {values: options, positionals} = parseArgs({
54
55
  'yes': {type: 'boolean', short: 'y'},
55
56
  'help': {type: 'boolean', short: 'h'},
56
57
  'module': {type: 'string'},
58
+ 'source': {type: 'string'},
57
59
  'label-es': {type: 'string'},
58
60
  'label-en': {type: 'string'},
59
61
  ...ANSWER_OPTIONS,
@@ -129,8 +131,8 @@ async function runMakeComponent(componentName) {
129
131
  }
130
132
 
131
133
  async function runCheckTexts() {
132
- const {problems, checkedKeys, textFiles, languages} = await checkTexts({projectRoot: cwd()});
133
- stdout.write(`Revisé ${checkedKeys} llaves escritas en el código contra ${textFiles.length} archivos de textos (${languages.join(', ') || 'sin idiomas'}).\n`);
134
+ const {problems, checkedKeys, textFiles, languages, sourceFolder} = await checkTexts({projectRoot: cwd(), sourceFolder: options.source});
135
+ stdout.write(`Revisé ${checkedKeys} llaves escritas en el código de ${sourceFolder}/ contra ${textFiles.length} archivos de textos (${languages.join(', ') || 'sin idiomas'}).\n`);
134
136
  if (problems.length === 0) {
135
137
  stdout.write('Todo en su lugar.\n');
136
138
  return;
package/cli/checkTexts.js CHANGED
@@ -8,12 +8,17 @@
8
8
  // Para esas está el aviso de Calcita en la consola (onMissingText de ToolkitProvider).
9
9
  //
10
10
  // Lee los archivos de textos del proyecto (src/**/*Texts.js y src/**/texts.js: cada uno exporta {es: {...}, en: {...}}) con
11
- // node, así que no deben importar otros archivos; el que los junta (con import.meta.glob) se salta. Los namespaces de Calcita
12
- // (common, table, validation…) salen de su dist/.
11
+ // node; el que los junta (con import.meta.glob) se salta. Si uno importa otro archivo, puede ser con ruta relativa o con el
12
+ // alias @/, que aquí apunta a la carpeta del código, como en vite.config.js (trampa pagada: un texts.js que reexportaba con
13
+ // @/ no se leía y sus 400 llaves salían como faltantes). Los namespaces de Calcita (common, table, validation…) salen de su dist/.
14
+ //
15
+ // Un verde falso es peor que un rojo: sin la carpeta, sin ningún archivo de textos o con uno que no se pudo leer, la revisión
16
+ // sale con problema y no compara llaves (con un archivo sin leer, todas las suyas saldrían como faltantes).
13
17
  //
14
18
  // Solo node, sin dependencias (el CLI corre antes de que el proyecto tenga nada instalado): function (regla de la casa).
15
19
  // Pruebas en checkTexts.test.js.
16
- import {readdir, readFile} from 'node:fs/promises';
20
+ import {readdir, readFile, stat} from 'node:fs/promises';
21
+ import * as nodeModule from 'node:module';
17
22
  import {join, relative} from 'node:path';
18
23
  import {pathToFileURL} from 'node:url';
19
24
 
@@ -27,6 +32,30 @@ const NAMESPACE_CALL = /useToolkitTranslation\(\s*\[?\s*(['"])([\w-]+)\1/g;
27
32
  const KEY_CALL = /\bt\(\s*(['"])([^'"`$\n]+?)\1([^)]{0,200})/g;
28
33
  const PLURAL_SUFFIXES = ['', '_zero', '_one', '_two', '_few', '_many', '_other'];
29
34
 
35
+ // la carpeta del código de la revisión en curso, para el alias @/ de los archivos de textos
36
+ let aliasFolderUrl = null;
37
+ let isAliasRegistered = false;
38
+
39
+ /**
40
+ * Que node entienda @/ al leer los archivos de textos, como Vite. Se registra una sola vez por proceso y lee aliasFolderUrl,
41
+ * la carpeta de la revisión en curso. registerHooks llegó en Node 22.15: con uno anterior, un archivo de textos que importe
42
+ * con @/ no se puede leer y se dice.
43
+ */
44
+ function understandAlias() {
45
+ if (isAliasRegistered || typeof nodeModule.registerHooks !== 'function') {
46
+ return;
47
+ }
48
+ nodeModule.registerHooks({
49
+ resolve(specifier, context, nextResolve) {
50
+ if (aliasFolderUrl && specifier.startsWith('@/')) {
51
+ return nextResolve(new URL(specifier.slice(2), aliasFolderUrl).href, context);
52
+ }
53
+ return nextResolve(specifier, context);
54
+ },
55
+ });
56
+ isAliasRegistered = true;
57
+ }
58
+
30
59
  /**
31
60
  * Los archivos de una carpeta y sus subcarpetas, sin node_modules ni dist.
32
61
  *
@@ -126,20 +155,31 @@ async function packageTextsOf() {
126
155
  /**
127
156
  * @param {object} options
128
157
  * @param {string} options.projectRoot la carpeta del proyecto
129
- * @param {string} [options.sourceFolder] dónde está el código (por omisión src)
158
+ * @param {string} [options.sourceFolder] dónde está el código (por omisión src; en Laravel, resources/js): `--source` del CLI
130
159
  * @param {Record<string, object>} [options.packageTexts] los textos de Calcita (por omisión, los de su dist/)
131
- * @returns {Promise<{problems: string[], checkedKeys: number, textFiles: string[], languages: string[]}>}
160
+ * @returns {Promise<{problems: string[], checkedKeys: number, textFiles: string[], languages: string[], sourceFolder: string}>}
132
161
  */
133
- export async function checkTexts({projectRoot, sourceFolder = 'src', packageTexts}) {
162
+ export async function checkTexts({projectRoot, sourceFolder: askedFolder = 'src', packageTexts}) {
163
+ // 'resources/js/' y 'resources/js' son la misma carpeta (el mensaje la dice sin la diagonal de más)
164
+ const sourceFolder = askedFolder.replace(/[\\/]+$/, '');
134
165
  const sourceRoot = join(projectRoot, sourceFolder);
166
+ const nothingChecked = {checkedKeys: 0, textFiles: [], languages: [], sourceFolder};
167
+ const isFolder = await stat(sourceRoot).then((found) => found.isDirectory(), () => false);
168
+ if (!isFolder) {
169
+ return {...nothingChecked, problems: [`No existe la carpeta ${sourceFolder}/ en el proyecto: no hay código que revisar (si está en otra, --source resources/js)`]};
170
+ }
135
171
  const files = await filesOf(sourceRoot);
136
172
  const problems = [];
173
+ aliasFolderUrl = `${pathToFileURL(sourceRoot).href}/`;
174
+ understandAlias();
137
175
 
138
176
  // 1. los textos del proyecto
139
177
  const projectTexts = {};
140
178
  const duplicates = [];
141
179
  const textFiles = files.filter((file) => TEXT_FILE.test(file.replaceAll('\\', '/')));
142
180
  const readTextFiles = [];
181
+ const unreadTextFiles = [];
182
+ const addedTexts = new Set();
143
183
  for (const file of textFiles) {
144
184
  const fileName = relative(projectRoot, file);
145
185
  // el que JUNTA los textos (appTexts.js, con el glob de Vite) no es un archivo de textos: se salta
@@ -153,11 +193,28 @@ export async function checkTexts({projectRoot, sourceFolder = 'src', packageText
153
193
  if (exported.length === 0) {
154
194
  problems.push(`${fileName}: no exporta textos con la forma {es: {...}, en: {...}}`);
155
195
  }
156
- exported.forEach((fileTexts) => addTexts(projectTexts, fileTexts, duplicates, fileName));
196
+ // el mismo objeto que otro archivo ya reexportó (texts.js que reexporta documentFileTexts.js) se suma una vez:
197
+ // node da el mismo objeto por archivo, y sumarlo dos veces lo haría pasar por repetido
198
+ exported.filter((fileTexts) => !addedTexts.has(fileTexts)).forEach((fileTexts) => {
199
+ addedTexts.add(fileTexts);
200
+ addTexts(projectTexts, fileTexts, duplicates, fileName);
201
+ });
157
202
  } catch (error) {
158
- problems.push(`${fileName}: no se pudo leer (${error.message.split('\n')[0]}); un archivo de textos no debe importar otros archivos`);
203
+ unreadTextFiles.push(fileName);
204
+ const reason = error.message.split('\n')[0];
205
+ const hint = isAliasRegistered
206
+ ? 'revise lo que importa: otro archivo va con ruta relativa o con @/'
207
+ : 'si importa con @/, esta revisión necesita Node 22.15 o más';
208
+ problems.push(`${fileName}: no se pudo leer (${reason}); ${hint}`);
159
209
  }
160
210
  }
211
+ if (unreadTextFiles.length > 0) {
212
+ problems.push(`No revisé las llaves: sin ${unreadTextFiles.length === 1 ? 'ese archivo' : 'esos archivos'} de textos, sus llaves saldrían todas como faltantes`);
213
+ return {...nothingChecked, problems, textFiles: readTextFiles};
214
+ }
215
+ if (readTextFiles.length === 0) {
216
+ return {...nothingChecked, problems: [`No encontré archivos de textos (*Texts.js o texts.js) en ${sourceFolder}/: no hay con qué comparar (si el código está en otra carpeta, --source resources/js)`]};
217
+ }
161
218
  problems.push(...duplicates.map((duplicate) => `texto repetido: ${duplicate}`));
162
219
 
163
220
  const languages = Object.keys(projectTexts);
@@ -205,5 +262,5 @@ export async function checkTexts({projectRoot, sourceFolder = 'src', packageText
205
262
  }
206
263
  }
207
264
 
208
- return {problems, checkedKeys, textFiles: readTextFiles, languages};
265
+ return {problems, checkedKeys, textFiles: readTextFiles, languages, sourceFolder};
209
266
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BarActionButton.js","sources":["../../src/appShell/BarActionButton.jsx"],"sourcesContent":["// Un botón de acción de la pantalla que se acomoda solo a donde quedó (lo pidió el autor, y es lo que pide la guía de Apple\n// para las barras: «Prefer simple, recognizable symbols for items instead of text»):\n// - en una cápsula (arriba en la barra, flotando en el teléfono o en la franja): solo el ícono, con su nombre en el tooltip\n// y para el lector de pantalla. En el teléfono el tooltip sale al dejar el dedo encima, como en iOS;\n// - en el menú «⋯» del teléfono (las que ya no caben en el renglón flotante): un renglón con ícono y texto;\n// - suelto (fuera de BarActions): ícono con texto, botón sin relleno.\n// Lo demás va al botón (disabled, color, component y to para una liga…). Sin ícono, en la cápsula va su texto (un botón sin\n// relleno) y en el «⋯» el renglón sin ícono: mejor un texto que un botón vacío.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaBarActionButton con defaultProps, styleOverrides de\n// sus partes (button, iconButton, menuItem) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\n\nimport {BAR_ACTIONS_PLACEMENT, useBarActionsPlacement, useCloseBarActionsMenu} from '@/appShell/barActionsSlot.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaBarActionButton)\nconst THEME_NAME = 'CalcitaBarActionButton';\nconst BarActionButtonButton = piecePart(THEME_NAME, 'Button', Button);\nconst BarActionButtonIconButton = piecePart(THEME_NAME, 'IconButton', IconButton);\nconst BarActionButtonMenuItem = piecePart(THEME_NAME, 'MenuItem', MenuItem);\n\n/**\n * @param {object} props\n * @param {string} props.label el nombre de la acción, ya traducido: «Agregar sucursal»\n * @param {import('react').ReactNode} [props.icon] el ícono (<AddRoundedIcon />); sin él, se ve su texto\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.disabled]\n * @param {'inherit'|'primary'|'secondary'|'success'|'error'|'info'|'warning'} [props.color] por omisión primary\n */\nfunction BarActionButtonContent(props) {\n const {label, icon, onClick, disabled, color = 'primary', ...buttonProps} = usePieceProps(props, THEME_NAME);\n const placement = useBarActionsPlacement();\n const closeMenu = useCloseBarActionsMenu();\n\n if (placement === BAR_ACTIONS_PLACEMENT.menu) {\n // primero se cierra el menú: la acción puede abrir una ventana y el menú quedaba encima\n const pickFromMenu = (event) => {\n closeMenu();\n onClick?.(event);\n };\n return (\n <BarActionButtonMenuItem onClick={pickFromMenu} disabled={disabled} {...buttonProps}>\n {icon && <ListItemIcon>{icon}</ListItemIcon>}\n <ListItemText inset={!icon}>{label}</ListItemText>\n </BarActionButtonMenuItem>\n );\n }\n if (placement === BAR_ACTIONS_PLACEMENT.capsule && icon) {\n // OJO: el tooltip envuelve un span para que también salga con el botón deshabilitado (MUI no lo pinta sobre un\n // elemento deshabilitado)\n return (\n <Tooltip title={label}>\n <span>\n <BarActionButtonIconButton aria-label={label} onClick={onClick} disabled={disabled} color={color} {...buttonProps}>{icon}</BarActionButtonIconButton>\n </span>\n </Tooltip>\n );\n }\n return <BarActionButtonButton startIcon={icon} onClick={onClick} disabled={disabled} color={color} {...buttonProps}>{label}</BarActionButtonButton>;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst BarActionButton = withPieceSlots(BarActionButtonContent, THEME_NAME);\nexport default BarActionButton;\n"],"names":[],"mappings":";;;;;;;;;;AAwBA,MAAM,aAAa;AACnB,MAAM,wBAAwB,UAAU,YAAY,UAAU,MAAM;AACpE,MAAM,4BAA4B,UAAU,YAAY,cAAc,UAAU;AAChF,MAAM,0BAA0B,UAAU,YAAY,YAAY,QAAQ;AAU1E,SAAS,uBAAuB,OAAO;AACnC,QAAM,EAAC,OAAO,MAAM,SAAS,UAAU,QAAQ,WAAW,GAAG,YAAA,IAAe,cAAc,OAAO,UAAU;AAC3G,QAAM,YAAY,uBAAA;AAClB,QAAM,YAAY,uBAAA;AAElB,MAAI,cAAc,sBAAsB,MAAM;AAE1C,UAAM,eAAe,CAAC,UAAU;AAC5B,gBAAA;AACA,gBAAU,KAAK;AAAA,IACnB;AACA,gCACK,yBAAA,EAAwB,SAAS,cAAc,UAAqB,GAAG,aACnE,UAAA;AAAA,MAAA,QAAQ,oBAAC,gBAAc,UAAA,KAAA,CAAK;AAAA,MAC7B,oBAAC,cAAA,EAAa,OAAO,CAAC,MAAO,UAAA,MAAA,CAAM;AAAA,IAAA,GACvC;AAAA,EAER;AACA,MAAI,cAAc,sBAAsB,WAAW,MAAM;AAGrD,+BACK,SAAA,EAAQ,OAAO,OACZ,UAAA,oBAAC,UACG,UAAA,oBAAC,2BAAA,EAA0B,cAAY,OAAO,SAAkB,UAAoB,OAAe,GAAG,aAAc,UAAA,MAAK,GAC7H,EAAA,CACJ;AAAA,EAER;AACA,SAAO,oBAAC,yBAAsB,WAAW,MAAM,SAAkB,UAAoB,OAAe,GAAG,aAAc,UAAA,MAAA,CAAM;AAC/H;AAGA,MAAM,kBAAkB,eAAe,wBAAwB,UAAU;"}
1
+ {"version":3,"file":"BarActionButton.js","sources":["../../src/appShell/BarActionButton.jsx"],"sourcesContent":["// Un botón de acción de la pantalla que se acomoda solo a donde quedó (lo pidió el autor, y es lo que pide la guía de Apple\n// para las barras: «Prefer simple, recognizable symbols for items instead of text»):\n// - en una cápsula (arriba en la barra, flotando en el teléfono o en la franja): solo el ícono, con su nombre en el tooltip\n// y para el lector de pantalla. En el teléfono el tooltip sale al dejar el dedo encima, como en iOS;\n// - en el menú «⋯» del teléfono (las que ya no caben en el renglón flotante): un renglón con ícono y texto;\n// - suelto (fuera de BarActions): ícono con texto, botón sin relleno.\n// Lo demás va al botón (disabled, color, component y to para una liga…). Sin ícono, en la cápsula va su texto (un botón sin\n// relleno) y en el «⋯» el renglón sin ícono: mejor un texto que un botón vacío.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaBarActionButton con defaultProps, styleOverrides de\n// sus partes (button, iconButton, menuItem) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\n\nimport {BAR_ACTIONS_PLACEMENT, useBarActionsPlacement, useCloseBarActionsMenu} from '@/appShell/barActionsSlot.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaBarActionButton)\nconst THEME_NAME = 'CalcitaBarActionButton';\nconst BarActionButtonButton = piecePart(THEME_NAME, 'Button', Button);\nconst BarActionButtonIconButton = piecePart(THEME_NAME, 'IconButton', IconButton);\nconst BarActionButtonMenuItem = piecePart(THEME_NAME, 'MenuItem', MenuItem);\n\n/**\n * @param {object} props\n * @param {string} props.label el nombre de la acción, ya traducido: «Agregar sucursal»\n * @param {import('react').ReactNode} [props.icon] el ícono (<AddRoundedIcon />); sin él, se ve su texto\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.disabled]\n * @param {'inherit'|'primary'|'secondary'|'success'|'error'|'info'|'warning'} [props.color] por omisión primary\n * @param {object} [props.sx] ajustes del botón; lo demás que se le pase también llega al botón (o al renglón del «⋯»)\n */\nfunction BarActionButtonContent(props) {\n const {label, icon, onClick, disabled, color = 'primary', ...buttonProps} = usePieceProps(props, THEME_NAME);\n const placement = useBarActionsPlacement();\n const closeMenu = useCloseBarActionsMenu();\n\n if (placement === BAR_ACTIONS_PLACEMENT.menu) {\n // primero se cierra el menú: la acción puede abrir una ventana y el menú quedaba encima\n const pickFromMenu = (event) => {\n closeMenu();\n onClick?.(event);\n };\n return (\n <BarActionButtonMenuItem onClick={pickFromMenu} disabled={disabled} {...buttonProps}>\n {icon && <ListItemIcon>{icon}</ListItemIcon>}\n <ListItemText inset={!icon}>{label}</ListItemText>\n </BarActionButtonMenuItem>\n );\n }\n if (placement === BAR_ACTIONS_PLACEMENT.capsule && icon) {\n // OJO: el tooltip envuelve un span para que también salga con el botón deshabilitado (MUI no lo pinta sobre un\n // elemento deshabilitado)\n return (\n <Tooltip title={label}>\n <span>\n <BarActionButtonIconButton aria-label={label} onClick={onClick} disabled={disabled} color={color} {...buttonProps}>{icon}</BarActionButtonIconButton>\n </span>\n </Tooltip>\n );\n }\n return <BarActionButtonButton startIcon={icon} onClick={onClick} disabled={disabled} color={color} {...buttonProps}>{label}</BarActionButtonButton>;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst BarActionButton = withPieceSlots(BarActionButtonContent, THEME_NAME);\nexport default BarActionButton;\n"],"names":[],"mappings":";;;;;;;;;;AAwBA,MAAM,aAAa;AACnB,MAAM,wBAAwB,UAAU,YAAY,UAAU,MAAM;AACpE,MAAM,4BAA4B,UAAU,YAAY,cAAc,UAAU;AAChF,MAAM,0BAA0B,UAAU,YAAY,YAAY,QAAQ;AAW1E,SAAS,uBAAuB,OAAO;AACnC,QAAM,EAAC,OAAO,MAAM,SAAS,UAAU,QAAQ,WAAW,GAAG,YAAA,IAAe,cAAc,OAAO,UAAU;AAC3G,QAAM,YAAY,uBAAA;AAClB,QAAM,YAAY,uBAAA;AAElB,MAAI,cAAc,sBAAsB,MAAM;AAE1C,UAAM,eAAe,CAAC,UAAU;AAC5B,gBAAA;AACA,gBAAU,KAAK;AAAA,IACnB;AACA,gCACK,yBAAA,EAAwB,SAAS,cAAc,UAAqB,GAAG,aACnE,UAAA;AAAA,MAAA,QAAQ,oBAAC,gBAAc,UAAA,KAAA,CAAK;AAAA,MAC7B,oBAAC,cAAA,EAAa,OAAO,CAAC,MAAO,UAAA,MAAA,CAAM;AAAA,IAAA,GACvC;AAAA,EAER;AACA,MAAI,cAAc,sBAAsB,WAAW,MAAM;AAGrD,+BACK,SAAA,EAAQ,OAAO,OACZ,UAAA,oBAAC,UACG,UAAA,oBAAC,2BAAA,EAA0B,cAAY,OAAO,SAAkB,UAAoB,OAAe,GAAG,aAAc,UAAA,MAAK,GAC7H,EAAA,CACJ;AAAA,EAER;AACA,SAAO,oBAAC,yBAAsB,WAAW,MAAM,SAAkB,UAAoB,OAAe,GAAG,aAAc,UAAA,MAAA,CAAM;AAC/H;AAGA,MAAM,kBAAkB,eAAe,wBAAwB,UAAU;"}
@@ -27,6 +27,8 @@ function DateInputContent(props) {
27
27
  disablePast = false,
28
28
  openTo = "day",
29
29
  disabled = false,
30
+ required = false,
31
+ format,
30
32
  inputRef,
31
33
  pickerProps,
32
34
  messages,
@@ -35,11 +37,11 @@ function DateInputContent(props) {
35
37
  } = usePieceProps(props, THEME_NAME);
36
38
  warnUnknownProps(THEME_NAME, unknownProps);
37
39
  const { language, inputId, errorText, formats } = usePickerField({ name, kind: "date", error, label, messages, texts });
38
- return /* @__PURE__ */ jsx(PickerFrame, { inputId, label, language, children: /* @__PURE__ */ jsx(
40
+ return /* @__PURE__ */ jsx(PickerFrame, { inputId, label, required, language, children: /* @__PURE__ */ jsx(
39
41
  DateInputPicker,
40
42
  {
41
43
  ...pickerProps,
42
- format: formats.date,
44
+ format: format ?? pickerProps?.format ?? formats.date,
43
45
  value: pickerValueOf(value),
44
46
  minDate: pickerValueOf(minDate) ?? void 0,
45
47
  maxDate: pickerValueOf(maxDate) ?? void 0,
@@ -54,7 +56,7 @@ function DateInputContent(props) {
54
56
  pickerProps?.onClose?.();
55
57
  onBlur?.();
56
58
  },
57
- slotProps: textFieldSlotPropsOf({ id: inputId, name, onBlur, error: Boolean(errorText), helperText: errorText ?? helperText }, pickerProps)
59
+ slotProps: textFieldSlotPropsOf({ id: inputId, name, onBlur, required, error: Boolean(errorText), helperText: errorText ?? helperText }, pickerProps)
58
60
  }
59
61
  ) });
60
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DateInput.js","sources":["../../src/datePickers/DateInput.jsx"],"sourcesContent":["// Campo de FECHA suelto con el DatePicker de MUI X (@stackcraftmx/calcita/date-pickers), con la etiqueta arriba como los\n// demás campos del paquete.\n//\n// Al formulario (y al servidor) viaja la fecha de calendario 'AAAA-MM-DD' en texto; en pantalla se ve DD/MM/AAAA en español\n// y MM/DD/AAAA en inglés, con el calendario y sus textos en el idioma activo. '' = sin fecha. Una fecha de calendario no\n// tiene hora ni zona: nunca se pasa por Date (que la movería de día según la zona).\n//\n// Cada campo trae su LocalizationProvider con dayjs (PickerFrame): la pantalla no tiene que poner nada alrededor.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDateInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport dayjs from 'dayjs';\nimport {DatePicker} from '@mui/x-date-pickers/DatePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {CALENDAR_DATE_FORMAT} from '@/datePickers/pickerValues.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDateInput)\nconst THEME_NAME = 'CalcitaDateInput';\nconst DateInputPicker = piecePart(THEME_NAME, 'Picker', DatePicker);\n\nexport {CALENDAR_DATE_FORMAT};\n\n/**\n * Una fecha de calendario 'AAAA-MM-DD' como la quiere el DatePicker; null si está vacía.\n *\n * @param {string|null|undefined} calendarDate\n * @returns {import('dayjs').Dayjs|null}\n */\nfunction pickerValueOf(calendarDate) {\n // dayjs lee 'AAAA-MM-DD' como día local (a diferencia de new Date, que lo toma en UTC y lo mueve de día)\n return calendarDate ? dayjs(calendarDate) : null;\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 'AAAA-MM-DD' o ''\n * @param {(value: string) => void} props.onChange recibe 'AAAA-MM-DD' ('' al borrar o con una fecha a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minDate] 'AAAA-MM-DD'\n * @param {string} [props.maxDate] 'AAAA-MM-DD'\n * @param {boolean} [props.disableFuture] p. ej. una fecha de nacimiento\n * @param {boolean} [props.disablePast] p. ej. una cita\n * @param {'day'|'month'|'year'} [props.openTo] por dónde abre el calendario (year para un nacimiento)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DatePicker de MUI X que aquí no tiene nombre (shouldDisableDate, slots,\n * slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction DateInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', minDate, maxDate, disableFuture = false, disablePast = false,\n openTo = 'day', disabled = false, inputRef, pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {language, inputId, errorText, formats} = usePickerField({name, kind: 'date', error, label, messages, texts});\n\n return (\n <PickerFrame inputId={inputId} label={label} language={language}>\n <DateInputPicker\n {...pickerProps}\n format={formats.date}\n value={pickerValueOf(value)}\n minDate={pickerValueOf(minDate) ?? undefined}\n maxDate={pickerValueOf(maxDate) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n openTo={openTo}\n views={openTo === 'year' ? ['year', 'month', 'day'] : pickerProps?.views}\n inputRef={inputRef}\n // una fecha a medias (25/1_/____) todavía no es fecha: viaja como vacía hasta que se complete\n onChange={(pickedDate) => onChange(pickedDate?.isValid() ? pickedDate.format(CALENDAR_DATE_FORMAT) : '')}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf({id: inputId, name, onBlur, error: Boolean(errorText), helperText: errorText ?? helperText}, pickerProps)}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst DateInput = withPieceSlots(DateInputContent, THEME_NAME);\nexport default DateInput;\n"],"names":[],"mappings":";;;;;;;;;AAuBA,MAAM,aAAa;AACnB,MAAM,kBAAkB,UAAU,YAAY,UAAU,UAAU;AAUlE,SAAS,cAAc,cAAc;AAEjC,SAAO,eAAe,MAAM,YAAY,IAAI;AAChD;AAwBA,SAAS,iBAAiB,OAAO;AAC7B,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAS;AAAA,IAAS,gBAAgB;AAAA,IAAO,cAAc;AAAA,IACtH,SAAS;AAAA,IAAO,WAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAAa;AAAA,IAAU;AAAA,IACnE,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,UAAU,SAAS,WAAW,YAAW,eAAe,EAAC,MAAM,MAAM,QAAQ,OAAO,OAAO,UAAU,OAAM;AAElH,SACI,oBAAC,aAAA,EAAY,SAAkB,OAAc,UACzC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MACJ,QAAQ,QAAQ;AAAA,MAChB,OAAO,cAAc,KAAK;AAAA,MAC1B,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,WAAW,SAAS,CAAC,QAAQ,SAAS,KAAK,IAAI,aAAa;AAAA,MACnE;AAAA,MAEA,UAAU,CAAC,eAAe,SAAS,YAAY,YAAY,WAAW,OAAO,oBAAoB,IAAI,EAAE;AAAA,MACvG,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW,qBAAqB,EAAC,IAAI,SAAS,MAAM,QAAQ,OAAO,QAAQ,SAAS,GAAG,YAAY,aAAa,WAAA,GAAa,WAAW;AAAA,IAAA;AAAA,EAAA,GAEhJ;AAER;AAGA,MAAM,YAAY,eAAe,kBAAkB,UAAU;"}
1
+ {"version":3,"file":"DateInput.js","sources":["../../src/datePickers/DateInput.jsx"],"sourcesContent":["// Campo de FECHA suelto con el DatePicker de MUI X (@stackcraftmx/calcita/date-pickers), con la etiqueta arriba como los\n// demás campos del paquete.\n//\n// Al formulario (y al servidor) viaja la fecha de calendario 'AAAA-MM-DD' en texto; en pantalla se ve DD/MM/AAAA en español\n// y MM/DD/AAAA en inglés, con el calendario y sus textos en el idioma activo. '' = sin fecha. Una fecha de calendario no\n// tiene hora ni zona: nunca se pasa por Date (que la movería de día según la zona).\n//\n// Cada campo trae su LocalizationProvider con dayjs (PickerFrame): la pantalla no tiene que poner nada alrededor.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDateInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport dayjs from 'dayjs';\nimport {DatePicker} from '@mui/x-date-pickers/DatePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {CALENDAR_DATE_FORMAT} from '@/datePickers/pickerValues.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDateInput)\nconst THEME_NAME = 'CalcitaDateInput';\nconst DateInputPicker = piecePart(THEME_NAME, 'Picker', DatePicker);\n\nexport {CALENDAR_DATE_FORMAT};\n\n/**\n * Una fecha de calendario 'AAAA-MM-DD' como la quiere el DatePicker; null si está vacía.\n *\n * @param {string|null|undefined} calendarDate\n * @returns {import('dayjs').Dayjs|null}\n */\nfunction pickerValueOf(calendarDate) {\n // dayjs lee 'AAAA-MM-DD' como día local (a diferencia de new Date, que lo toma en UTC y lo mueve de día)\n return calendarDate ? dayjs(calendarDate) : null;\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 'AAAA-MM-DD' o ''\n * @param {(value: string) => void} props.onChange recibe 'AAAA-MM-DD' ('' al borrar o con una fecha a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minDate] 'AAAA-MM-DD'\n * @param {string} [props.maxDate] 'AAAA-MM-DD'\n * @param {boolean} [props.disableFuture] p. ej. una fecha de nacimiento\n * @param {boolean} [props.disablePast] p. ej. una cita\n * @param {'day'|'month'|'year'} [props.openTo] por dónde abre el calendario (year para un nacimiento)\n * @param {boolean} [props.disabled]\n * @param {string} [props.format] cómo se ve la fecha, con los códigos de dayjs ('DD/MMM/YYYY' enseña «16/jul/2029», sin dudas\n * entre día y mes); por omisión DD/MM/YYYY en español y MM/DD/YYYY en inglés\n * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta y, como en MUI, required en el campo\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DatePicker de MUI X que aquí no tiene nombre (shouldDisableDate, slots,\n * slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction DateInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', minDate, maxDate, disableFuture = false, disablePast = false,\n openTo = 'day', disabled = false, required = false, format, inputRef, pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {language, inputId, errorText, formats} = usePickerField({name, kind: 'date', error, label, messages, texts});\n\n return (\n <PickerFrame inputId={inputId} label={label} required={required} language={language}>\n <DateInputPicker\n {...pickerProps}\n // el formato de la pantalla gana al de la casa (antes el de la casa iba después y lo pisaba sin avisar)\n format={format ?? pickerProps?.format ?? formats.date}\n value={pickerValueOf(value)}\n minDate={pickerValueOf(minDate) ?? undefined}\n maxDate={pickerValueOf(maxDate) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n openTo={openTo}\n views={openTo === 'year' ? ['year', 'month', 'day'] : pickerProps?.views}\n inputRef={inputRef}\n // una fecha a medias (25/1_/____) todavía no es fecha: viaja como vacía hasta que se complete\n onChange={(pickedDate) => onChange(pickedDate?.isValid() ? pickedDate.format(CALENDAR_DATE_FORMAT) : '')}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf({id: inputId, name, onBlur, required, error: Boolean(errorText), helperText: errorText ?? helperText}, pickerProps)}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst DateInput = withPieceSlots(DateInputContent, THEME_NAME);\nexport default DateInput;\n"],"names":[],"mappings":";;;;;;;;;AAuBA,MAAM,aAAa;AACnB,MAAM,kBAAkB,UAAU,YAAY,UAAU,UAAU;AAUlE,SAAS,cAAc,cAAc;AAEjC,SAAO,eAAe,MAAM,YAAY,IAAI;AAChD;AA2BA,SAAS,iBAAiB,OAAO;AAC7B,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAS;AAAA,IAAS,gBAAgB;AAAA,IAAO,cAAc;AAAA,IACtH,SAAS;AAAA,IAAO,WAAW;AAAA,IAAO,WAAW;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAU;AAAA,IAAa;AAAA,IAAU;AAAA,IAC7F,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,UAAU,SAAS,WAAW,YAAW,eAAe,EAAC,MAAM,MAAM,QAAQ,OAAO,OAAO,UAAU,OAAM;AAElH,SACI,oBAAC,aAAA,EAAY,SAAkB,OAAc,UAAoB,UAC7D,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MAEJ,QAAQ,UAAU,aAAa,UAAU,QAAQ;AAAA,MACjD,OAAO,cAAc,KAAK;AAAA,MAC1B,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC,SAAS,cAAc,OAAO,KAAK;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,WAAW,SAAS,CAAC,QAAQ,SAAS,KAAK,IAAI,aAAa;AAAA,MACnE;AAAA,MAEA,UAAU,CAAC,eAAe,SAAS,YAAY,YAAY,WAAW,OAAO,oBAAoB,IAAI,EAAE;AAAA,MACvG,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW,qBAAqB,EAAC,IAAI,SAAS,MAAM,QAAQ,UAAU,OAAO,QAAQ,SAAS,GAAG,YAAY,aAAa,WAAA,GAAa,WAAW;AAAA,IAAA;AAAA,EAAA,GAE1J;AAER;AAGA,MAAM,YAAY,eAAe,kBAAkB,UAAU;"}
@@ -31,6 +31,8 @@ function DateTimeInputContent(props) {
31
31
  showTimeZone = true,
32
32
  timeZoneLabel,
33
33
  disabled = false,
34
+ required = false,
35
+ format,
34
36
  inputRef,
35
37
  pickerProps,
36
38
  messages,
@@ -46,13 +48,13 @@ function DateTimeInputContent(props) {
46
48
  const hasClockRule = minimumMinutesFromNow != null;
47
49
  const currentMinute = useCurrentMinute(hasClockRule);
48
50
  const earliest = latestInstantOf(minDateTime, hasClockRule ? earliestInstantFromNow(minimumMinutesFromNow, currentMinute) : "");
49
- return /* @__PURE__ */ jsx(PickerFrame, { inputId: field.inputId, label, language: field.language, children: /* @__PURE__ */ jsx(
51
+ return /* @__PURE__ */ jsx(PickerFrame, { inputId: field.inputId, label, required, language: field.language, children: /* @__PURE__ */ jsx(
50
52
  DateTimeInputPicker,
51
53
  {
52
54
  ...pickerProps,
53
55
  timezone: shownTimeZone,
54
- format: field.formats.dateTime,
55
- ampm: field.formats.ampm,
56
+ format: format ?? pickerProps?.format ?? field.formats.dateTime,
57
+ ampm: pickerProps?.ampm ?? field.formats.ampm,
56
58
  value: pickerInstantOf(value, shownTimeZone),
57
59
  minDateTime: pickerInstantOf(earliest, shownTimeZone) ?? void 0,
58
60
  maxDateTime: pickerInstantOf(maxDateTime, shownTimeZone) ?? void 0,
@@ -68,7 +70,7 @@ function DateTimeInputContent(props) {
68
70
  onBlur?.();
69
71
  },
70
72
  slotProps: textFieldSlotPropsOf(
71
- { id: field.inputId, name, onBlur, error: Boolean(field.errorText), helperText: field.errorText ?? helperLine },
73
+ { id: field.inputId, name, onBlur, required, error: Boolean(field.errorText), helperText: field.errorText ?? helperLine },
72
74
  pickerProps
73
75
  )
74
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.js","sources":["../../src/datePickers/DateTimeInput.jsx"],"sourcesContent":["// Campo de FECHA CON HORA suelto con el DateTimePicker de MUI X (@stackcraftmx/calcita/date-pickers), para un instante:\n// una cita, un envío programado, «posponer hasta».\n//\n// Viaja como instante en UTC ('2026-11-01T18:30:00Z') y se pinta en la zona del proveedor (ToolkitProvider timeZone),\n// rotulada abajo con su nombre («Hora estándar central»): quien lo ve sabe en qué hora está escribiendo aunque su equipo\n// esté en otra zona. Lee también lo que llega del servidor con desfase ('…-06:00'). '' = vacío. La hora sale de 24 horas o\n// con a.m. y p.m. según el hourCycle del proveedor.\n//\n// Los minutos van de uno en uno (minutesStep los cambia). Para «al menos N minutos después de ahora» (publicar con\n// anticipación) está minimumMinutesFromNow: el mínimo se recorre solo con el reloj mientras la pantalla está abierta. Para\n// «el fin, al menos N minutos después del inicio», minDateTime={instantPlusMinutes(startsAt, N)}. Con formik, las mismas\n// reglas en yup: minutesFromNowRule y minutesAfterRule (el servidor vuelve a revisar).\n//\n// Para una hora del día sin fecha («abre a las 09:00») va TimeInput; para una fecha sin hora, DateInput.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDateTimeInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {DateTimePicker} from '@mui/x-date-pickers/DateTimePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {\n DEFAULT_MINUTES_STEP, earliestInstantFromNow, instantTextOf, latestInstantOf, pickerInstantOf, timeZoneNameOf,\n} from '@/datePickers/pickerValues.js';\nimport {useCurrentMinute} from '@/datePickers/useCurrentMinute.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {intlLocaleOf} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDateTimeInput)\nconst THEME_NAME = 'CalcitaDateTimeInput';\nconst DateTimeInputPicker = piecePart(THEME_NAME, 'Picker', DateTimePicker);\n\n// entre la ayuda de la pantalla y el nombre de la zona\nconst HELPER_SEPARATOR = ' · ';\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 el instante ('2026-11-01T18:30:00Z' o con desfase) o ''\n * @param {(value: string) => void} props.onChange recibe el instante en UTC ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida; va antes del nombre de la zona\n * @param {string} [props.minDateTime] instante (el fin de algo: instantPlusMinutes(startsAt, 30))\n * @param {number} [props.minimumMinutesFromNow] al menos tantos minutos después de ahora; se recorre con el reloj\n * @param {string} [props.maxDateTime] instante\n * @param {boolean} [props.disableFuture]\n * @param {boolean} [props.disablePast] p. ej. un envío programado\n * @param {number} [props.minutesStep] de cuánto en cuánto van los minutos; por omisión de uno en uno (15 para citas de\n * cuarto de hora)\n * @param {string} [props.timeZone] otra zona que la del proveedor (la de una sucursal); rotulada igual\n * @param {boolean} [props.showTimeZone] false quita el nombre de la zona de abajo (por omisión se ve)\n * @param {string} [props.timeZoneLabel] ya traducido, en lugar del nombre que da el navegador\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DateTimePicker de MUI X que aquí no tiene nombre (views, shouldDisableDate,\n * slots, slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction DateTimeInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText, minDateTime, minimumMinutesFromNow, maxDateTime, disableFuture = false,\n disablePast = false, minutesStep = DEFAULT_MINUTES_STEP, timeZone, showTimeZone = true, timeZoneLabel, disabled = false, inputRef, pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const field = usePickerField({name, kind: 'dateTime', error, label, messages, texts});\n const {languages} = useToolkitSettings();\n const shownTimeZone = timeZone ?? field.timeZone;\n const zoneText = showTimeZone ? (timeZoneLabel ?? timeZoneNameOf(shownTimeZone, intlLocaleOf(field.language, languages))) : null;\n const helperLine = [helperText, zoneText].filter(Boolean).join(HELPER_SEPARATOR) || ' ';\n // el mínimo que de verdad aplica: el que da la pantalla o el de «ahora + N minutos», el más tarde\n const hasClockRule = minimumMinutesFromNow != null;\n const currentMinute = useCurrentMinute(hasClockRule);\n const earliest = latestInstantOf(minDateTime, hasClockRule ? earliestInstantFromNow(minimumMinutesFromNow, currentMinute) : '');\n\n return (\n <PickerFrame inputId={field.inputId} label={label} language={field.language}>\n <DateTimeInputPicker\n {...pickerProps}\n timezone={shownTimeZone}\n format={field.formats.dateTime}\n ampm={field.formats.ampm}\n value={pickerInstantOf(value, shownTimeZone)}\n minDateTime={pickerInstantOf(earliest, shownTimeZone) ?? undefined}\n maxDateTime={pickerInstantOf(maxDateTime, shownTimeZone) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n minutesStep={minutesStep}\n timeSteps={{minutes: minutesStep, ...pickerProps?.timeSteps}}\n inputRef={inputRef}\n onChange={(pickedValue) => onChange(instantTextOf(pickedValue))}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf(\n {id: field.inputId, name, onBlur, error: Boolean(field.errorText), helperText: field.errorText ?? helperLine},\n pickerProps,\n )}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst DateTimeInput = withPieceSlots(DateTimeInputContent, THEME_NAME);\nexport default DateTimeInput;\n"],"names":[],"mappings":";;;;;;;;;;;AAiCA,MAAM,aAAa;AACnB,MAAM,sBAAsB,UAAU,YAAY,UAAU,cAAc;AAG1E,MAAM,mBAAmB;AA6BzB,SAAS,qBAAqB,OAAO;AACjC,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAY;AAAA,IAAa;AAAA,IAAuB;AAAA,IAAa,gBAAgB;AAAA,IAC1H,cAAc;AAAA,IAAO,cAAc;AAAA,IAAsB;AAAA,IAAU,eAAe;AAAA,IAAM;AAAA,IAAe,WAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAAa;AAAA,IAAU;AAAA,IAC1J,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,eAAe,EAAC,MAAM,MAAM,YAAY,OAAO,OAAO,UAAU,OAAM;AACpF,QAAM,EAAC,UAAA,IAAa,mBAAA;AACpB,QAAM,gBAAgB,YAAY,MAAM;AACxC,QAAM,WAAW,eAAgB,iBAAiB,eAAe,eAAe,aAAa,MAAM,UAAU,SAAS,CAAC,IAAK;AAC5H,QAAM,aAAa,CAAC,YAAY,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,gBAAgB,KAAK;AAEpF,QAAM,eAAe,yBAAyB;AAC9C,QAAM,gBAAgB,iBAAiB,YAAY;AACnD,QAAM,WAAW,gBAAgB,aAAa,eAAe,uBAAuB,uBAAuB,aAAa,IAAI,EAAE;AAE9H,SACI,oBAAC,eAAY,SAAS,MAAM,SAAS,OAAc,UAAU,MAAM,UAC/D,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MACJ,UAAU;AAAA,MACV,QAAQ,MAAM,QAAQ;AAAA,MACtB,MAAM,MAAM,QAAQ;AAAA,MACpB,OAAO,gBAAgB,OAAO,aAAa;AAAA,MAC3C,aAAa,gBAAgB,UAAU,aAAa,KAAK;AAAA,MACzD,aAAa,gBAAgB,aAAa,aAAa,KAAK;AAAA,MAC5D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,EAAC,SAAS,aAAa,GAAG,aAAa,UAAA;AAAA,MAClD;AAAA,MACA,UAAU,CAAC,gBAAgB,SAAS,cAAc,WAAW,CAAC;AAAA,MAC9D,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACP,EAAC,IAAI,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS,GAAG,YAAY,MAAM,aAAa,WAAA;AAAA,QAClG;AAAA,MAAA;AAAA,IACJ;AAAA,EAAA,GAER;AAER;AAGA,MAAM,gBAAgB,eAAe,sBAAsB,UAAU;"}
1
+ {"version":3,"file":"DateTimeInput.js","sources":["../../src/datePickers/DateTimeInput.jsx"],"sourcesContent":["// Campo de FECHA CON HORA suelto con el DateTimePicker de MUI X (@stackcraftmx/calcita/date-pickers), para un instante:\n// una cita, un envío programado, «posponer hasta».\n//\n// Viaja como instante en UTC ('2026-11-01T18:30:00Z') y se pinta en la zona del proveedor (ToolkitProvider timeZone),\n// rotulada abajo con su nombre («Hora estándar central»): quien lo ve sabe en qué hora está escribiendo aunque su equipo\n// esté en otra zona. Lee también lo que llega del servidor con desfase ('…-06:00'). '' = vacío. La hora sale de 24 horas o\n// con a.m. y p.m. según el hourCycle del proveedor.\n//\n// Los minutos van de uno en uno (minutesStep los cambia). Para «al menos N minutos después de ahora» (publicar con\n// anticipación) está minimumMinutesFromNow: el mínimo se recorre solo con el reloj mientras la pantalla está abierta. Para\n// «el fin, al menos N minutos después del inicio», minDateTime={instantPlusMinutes(startsAt, N)}. Con formik, las mismas\n// reglas en yup: minutesFromNowRule y minutesAfterRule (el servidor vuelve a revisar).\n//\n// Para una hora del día sin fecha («abre a las 09:00») va TimeInput; para una fecha sin hora, DateInput.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDateTimeInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {DateTimePicker} from '@mui/x-date-pickers/DateTimePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {\n DEFAULT_MINUTES_STEP, earliestInstantFromNow, instantTextOf, latestInstantOf, pickerInstantOf, timeZoneNameOf,\n} from '@/datePickers/pickerValues.js';\nimport {useCurrentMinute} from '@/datePickers/useCurrentMinute.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {intlLocaleOf} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDateTimeInput)\nconst THEME_NAME = 'CalcitaDateTimeInput';\nconst DateTimeInputPicker = piecePart(THEME_NAME, 'Picker', DateTimePicker);\n\n// entre la ayuda de la pantalla y el nombre de la zona\nconst HELPER_SEPARATOR = ' · ';\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 el instante ('2026-11-01T18:30:00Z' o con desfase) o ''\n * @param {(value: string) => void} props.onChange recibe el instante en UTC ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida; va antes del nombre de la zona\n * @param {string} [props.minDateTime] instante (el fin de algo: instantPlusMinutes(startsAt, 30))\n * @param {number} [props.minimumMinutesFromNow] al menos tantos minutos después de ahora; se recorre con el reloj\n * @param {string} [props.maxDateTime] instante\n * @param {boolean} [props.disableFuture]\n * @param {boolean} [props.disablePast] p. ej. un envío programado\n * @param {number} [props.minutesStep] de cuánto en cuánto van los minutos; por omisión de uno en uno (15 para citas de\n * cuarto de hora)\n * @param {string} [props.timeZone] otra zona que la del proveedor (la de una sucursal); rotulada igual\n * @param {boolean} [props.showTimeZone] false quita el nombre de la zona de abajo (por omisión se ve)\n * @param {string} [props.timeZoneLabel] ya traducido, en lugar del nombre que da el navegador\n * @param {boolean} [props.disabled]\n * @param {string} [props.format] cómo se ven la fecha y la hora, con los códigos de dayjs ('DD/MMM/YYYY HH:mm'); por omisión\n * DD/MM/YYYY HH:mm en español y el reloj de ToolkitProvider hourCycle\n * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta y, como en MUI, required en el campo\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DateTimePicker de MUI X que aquí no tiene nombre (views, shouldDisableDate,\n * slots, slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction DateTimeInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText, minDateTime, minimumMinutesFromNow, maxDateTime, disableFuture = false,\n disablePast = false, minutesStep = DEFAULT_MINUTES_STEP, timeZone, showTimeZone = true, timeZoneLabel, disabled = false, required = false, format, inputRef, pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const field = usePickerField({name, kind: 'dateTime', error, label, messages, texts});\n const {languages} = useToolkitSettings();\n const shownTimeZone = timeZone ?? field.timeZone;\n const zoneText = showTimeZone ? (timeZoneLabel ?? timeZoneNameOf(shownTimeZone, intlLocaleOf(field.language, languages))) : null;\n const helperLine = [helperText, zoneText].filter(Boolean).join(HELPER_SEPARATOR) || ' ';\n // el mínimo que de verdad aplica: el que da la pantalla o el de «ahora + N minutos», el más tarde\n const hasClockRule = minimumMinutesFromNow != null;\n const currentMinute = useCurrentMinute(hasClockRule);\n const earliest = latestInstantOf(minDateTime, hasClockRule ? earliestInstantFromNow(minimumMinutesFromNow, currentMinute) : '');\n\n return (\n <PickerFrame inputId={field.inputId} label={label} required={required} language={field.language}>\n <DateTimeInputPicker\n {...pickerProps}\n timezone={shownTimeZone}\n // el formato y el reloj de la pantalla ganan a los de la casa (antes iban después y los pisaban sin avisar)\n format={format ?? pickerProps?.format ?? field.formats.dateTime}\n ampm={pickerProps?.ampm ?? field.formats.ampm}\n value={pickerInstantOf(value, shownTimeZone)}\n minDateTime={pickerInstantOf(earliest, shownTimeZone) ?? undefined}\n maxDateTime={pickerInstantOf(maxDateTime, shownTimeZone) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n minutesStep={minutesStep}\n timeSteps={{minutes: minutesStep, ...pickerProps?.timeSteps}}\n inputRef={inputRef}\n onChange={(pickedValue) => onChange(instantTextOf(pickedValue))}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf(\n {id: field.inputId, name, onBlur, required, error: Boolean(field.errorText), helperText: field.errorText ?? helperLine},\n pickerProps,\n )}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst DateTimeInput = withPieceSlots(DateTimeInputContent, THEME_NAME);\nexport default DateTimeInput;\n"],"names":[],"mappings":";;;;;;;;;;;AAiCA,MAAM,aAAa;AACnB,MAAM,sBAAsB,UAAU,YAAY,UAAU,cAAc;AAG1E,MAAM,mBAAmB;AAgCzB,SAAS,qBAAqB,OAAO;AACjC,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAY;AAAA,IAAa;AAAA,IAAuB;AAAA,IAAa,gBAAgB;AAAA,IAC1H,cAAc;AAAA,IAAO,cAAc;AAAA,IAAsB;AAAA,IAAU,eAAe;AAAA,IAAM;AAAA,IAAe,WAAW;AAAA,IAAO,WAAW;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAU;AAAA,IAAa;AAAA,IAAU;AAAA,IACpL,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,eAAe,EAAC,MAAM,MAAM,YAAY,OAAO,OAAO,UAAU,OAAM;AACpF,QAAM,EAAC,UAAA,IAAa,mBAAA;AACpB,QAAM,gBAAgB,YAAY,MAAM;AACxC,QAAM,WAAW,eAAgB,iBAAiB,eAAe,eAAe,aAAa,MAAM,UAAU,SAAS,CAAC,IAAK;AAC5H,QAAM,aAAa,CAAC,YAAY,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,gBAAgB,KAAK;AAEpF,QAAM,eAAe,yBAAyB;AAC9C,QAAM,gBAAgB,iBAAiB,YAAY;AACnD,QAAM,WAAW,gBAAgB,aAAa,eAAe,uBAAuB,uBAAuB,aAAa,IAAI,EAAE;AAE9H,SACI,oBAAC,eAAY,SAAS,MAAM,SAAS,OAAc,UAAoB,UAAU,MAAM,UACnF,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MACJ,UAAU;AAAA,MAEV,QAAQ,UAAU,aAAa,UAAU,MAAM,QAAQ;AAAA,MACvD,MAAM,aAAa,QAAQ,MAAM,QAAQ;AAAA,MACzC,OAAO,gBAAgB,OAAO,aAAa;AAAA,MAC3C,aAAa,gBAAgB,UAAU,aAAa,KAAK;AAAA,MACzD,aAAa,gBAAgB,aAAa,aAAa,KAAK;AAAA,MAC5D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,EAAC,SAAS,aAAa,GAAG,aAAa,UAAA;AAAA,MAClD;AAAA,MACA,UAAU,CAAC,gBAAgB,SAAS,cAAc,WAAW,CAAC;AAAA,MAC9D,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACP,EAAC,IAAI,MAAM,SAAS,MAAM,QAAQ,UAAU,OAAO,QAAQ,MAAM,SAAS,GAAG,YAAY,MAAM,aAAa,WAAA;AAAA,QAC5G;AAAA,MAAA;AAAA,IACJ;AAAA,EAAA,GAER;AAER;AAGA,MAAM,gBAAgB,eAAe,sBAAsB,UAAU;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import DateInput from "./DateInput.js";
4
4
  import { useFormikField } from "../forms/useFormikField.js";
5
5
  function FormDateField({ name, ...dateInputProps }) {
6
- const fieldProps = useFormikField(name);
6
+ const fieldProps = useFormikField(name, dateInputProps);
7
7
  return /* @__PURE__ */ jsx(DateInput, { ...dateInputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormDateField.js","sources":["../../src/datePickers/FormDateField.jsx"],"sourcesContent":["// Campo de fecha de formik: DateInput conectado a su campo. Al formulario llega 'AAAA-MM-DD'.\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): DateInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport DateInput from '@/datePickers/DateInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/DateInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las de\n * DateInput (minDate, maxDate, disableFuture, openTo…), sin value / onChange / onBlur / error\n */\nexport default function FormDateField({name, ...dateInputProps}) {\n const fieldProps = useFormikField(name);\n return <DateInput {...dateInputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,cAAc,EAAC,MAAM,GAAG,kBAAiB;AAC7D,QAAM,aAAa,eAAe,IAAI;AACtC,SAAO,oBAAC,WAAA,EAAW,GAAG,gBAAiB,GAAG,WAAA,CAAY;AAC1D;"}
1
+ {"version":3,"file":"FormDateField.js","sources":["../../src/datePickers/FormDateField.jsx"],"sourcesContent":["// Campo de fecha de formik: DateInput conectado a su campo. Al formulario llega 'AAAA-MM-DD'.\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): DateInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport DateInput from '@/datePickers/DateInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/DateInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/datePickers/DateInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las de\n * DateInput (minDate, maxDate, disableFuture, openTo…), sin value ni error (su onChange y su onBlur corren después de los del formulario)\n */\nexport default function FormDateField({name, ...dateInputProps}) {\n const fieldProps = useFormikField(name, dateInputProps);\n return <DateInput {...dateInputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,cAAc,EAAC,MAAM,GAAG,kBAAiB;AAC7D,QAAM,aAAa,eAAe,MAAM,cAAc;AACtD,SAAO,oBAAC,WAAA,EAAW,GAAG,gBAAiB,GAAG,WAAA,CAAY;AAC1D;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import DateTimeInput from "./DateTimeInput.js";
4
4
  import { useFormikField } from "../forms/useFormikField.js";
5
5
  function FormDateTimeField({ name, ...inputProps }) {
6
- const fieldProps = useFormikField(name);
6
+ const fieldProps = useFormikField(name, inputProps);
7
7
  return /* @__PURE__ */ jsx(DateTimeInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormDateTimeField.js","sources":["../../src/datePickers/FormDateTimeField.jsx"],"sourcesContent":["// Campo de fecha con hora de formik: DateTimeInput conectado a su campo. Al formulario llega el instante en\n// UTC ('2026-11-01T18:30:00Z').\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): DateTimeInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport DateTimeInput from '@/datePickers/DateTimeInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/DateTimeInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las de\n * DateTimeInput (minDateTime, minimumMinutesFromNow, disablePast, minutesStep, timeZone…), sin value / onChange / onBlur / error\n */\nexport default function FormDateTimeField({name, ...inputProps}) {\n const fieldProps = useFormikField(name);\n return <DateTimeInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAaA,SAAwB,kBAAkB,EAAC,MAAM,GAAG,cAAa;AAC7D,QAAM,aAAa,eAAe,IAAI;AACtC,SAAO,oBAAC,eAAA,EAAe,GAAG,YAAa,GAAG,WAAA,CAAY;AAC1D;"}
1
+ {"version":3,"file":"FormDateTimeField.js","sources":["../../src/datePickers/FormDateTimeField.jsx"],"sourcesContent":["// Campo de fecha con hora de formik: DateTimeInput conectado a su campo. Al formulario llega el instante en\n// UTC ('2026-11-01T18:30:00Z').\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): DateTimeInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport DateTimeInput from '@/datePickers/DateTimeInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/DateTimeInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/datePickers/DateTimeInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las de\n * DateTimeInput (minDateTime, minimumMinutesFromNow, disablePast, minutesStep, timeZone…), sin value ni error (su onChange y su onBlur corren después de los del formulario)\n */\nexport default function FormDateTimeField({name, ...inputProps}) {\n const fieldProps = useFormikField(name, inputProps);\n return <DateTimeInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAaA,SAAwB,kBAAkB,EAAC,MAAM,GAAG,cAAa;AAC7D,QAAM,aAAa,eAAe,MAAM,UAAU;AAClD,SAAO,oBAAC,eAAA,EAAe,GAAG,YAAa,GAAG,WAAA,CAAY;AAC1D;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import MonthInput from "./MonthInput.js";
4
4
  import { useFormikField } from "../forms/useFormikField.js";
5
5
  function FormMonthField({ name, ...inputProps }) {
6
- const fieldProps = useFormikField(name);
6
+ const fieldProps = useFormikField(name, inputProps);
7
7
  return /* @__PURE__ */ jsx(MonthInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormMonthField.js","sources":["../../src/datePickers/FormMonthField.jsx"],"sourcesContent":["// Campo de mes de formik: MonthInput conectado a su campo. Al formulario llega 'AAAA-MM'.\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): MonthInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport MonthInput from '@/datePickers/MonthInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/MonthInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las de\n * MonthInput (minMonth, maxMonth, disableFuture…), sin value / onChange / onBlur / error\n */\nexport default function FormMonthField({name, ...inputProps}) {\n const fieldProps = useFormikField(name);\n return <MonthInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,eAAe,EAAC,MAAM,GAAG,cAAa;AAC1D,QAAM,aAAa,eAAe,IAAI;AACtC,SAAO,oBAAC,YAAA,EAAY,GAAG,YAAa,GAAG,WAAA,CAAY;AACvD;"}
1
+ {"version":3,"file":"FormMonthField.js","sources":["../../src/datePickers/FormMonthField.jsx"],"sourcesContent":["// Campo de mes de formik: MonthInput conectado a su campo. Al formulario llega 'AAAA-MM'.\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): MonthInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport MonthInput from '@/datePickers/MonthInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/MonthInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/datePickers/MonthInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las de\n * MonthInput (minMonth, maxMonth, disableFuture…), sin value ni error (su onChange y su onBlur corren después de los del formulario)\n */\nexport default function FormMonthField({name, ...inputProps}) {\n const fieldProps = useFormikField(name, inputProps);\n return <MonthInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,eAAe,EAAC,MAAM,GAAG,cAAa;AAC1D,QAAM,aAAa,eAAe,MAAM,UAAU;AAClD,SAAO,oBAAC,YAAA,EAAY,GAAG,YAAa,GAAG,WAAA,CAAY;AACvD;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import TimeInput from "./TimeInput.js";
4
4
  import { useFormikField } from "../forms/useFormikField.js";
5
5
  function FormTimeField({ name, ...inputProps }) {
6
- const fieldProps = useFormikField(name);
6
+ const fieldProps = useFormikField(name, inputProps);
7
7
  return /* @__PURE__ */ jsx(TimeInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormTimeField.js","sources":["../../src/datePickers/FormTimeField.jsx"],"sourcesContent":["// Campo de hora de formik: TimeInput conectado a su campo. Al formulario llega 'HH:mm'.\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): TimeInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport TimeInput from '@/datePickers/TimeInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/TimeInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las de\n * TimeInput (minTime, maxTime, minutesStep…), sin value / onChange / onBlur / error\n */\nexport default function FormTimeField({name, ...inputProps}) {\n const fieldProps = useFormikField(name);\n return <TimeInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,cAAc,EAAC,MAAM,GAAG,cAAa;AACzD,QAAM,aAAa,eAAe,IAAI;AACtC,SAAO,oBAAC,WAAA,EAAW,GAAG,YAAa,GAAG,WAAA,CAAY;AACtD;"}
1
+ {"version":3,"file":"FormTimeField.js","sources":["../../src/datePickers/FormTimeField.jsx"],"sourcesContent":["// Campo de hora de formik: TimeInput conectado a su campo. Al formulario llega 'HH:mm'.\n//\n// Con react-hook-form no hay uno listo (esta puerta no pide react-hook-form): TimeInput va dentro de un Controller.\n//\n// Genérico del paquete: function (regla de la casa).\nimport TimeInput from '@/datePickers/TimeInput.jsx';\nimport {useFormikField} from '@/forms/useFormikField.js';\n\n/**\n * @param {{name: string} & Omit<Parameters<typeof import('@/datePickers/TimeInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/datePickers/TimeInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las de\n * TimeInput (minTime, maxTime, minutesStep…), sin value ni error (su onChange y su onBlur corren después de los del formulario)\n */\nexport default function FormTimeField({name, ...inputProps}) {\n const fieldProps = useFormikField(name, inputProps);\n return <TimeInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,cAAc,EAAC,MAAM,GAAG,cAAa;AACzD,QAAM,aAAa,eAAe,MAAM,UAAU;AAClD,SAAO,oBAAC,WAAA,EAAW,GAAG,YAAa,GAAG,WAAA,CAAY;AACtD;"}
@@ -23,6 +23,8 @@ function MonthInputContent(props) {
23
23
  disableFuture = false,
24
24
  disablePast = false,
25
25
  disabled = false,
26
+ required = false,
27
+ format,
26
28
  inputRef,
27
29
  pickerProps,
28
30
  messages,
@@ -31,13 +33,13 @@ function MonthInputContent(props) {
31
33
  } = usePieceProps(props, THEME_NAME);
32
34
  warnUnknownProps(THEME_NAME, unknownProps);
33
35
  const field = usePickerField({ name, kind: "month", error, label, messages, texts });
34
- return /* @__PURE__ */ jsx(PickerFrame, { inputId: field.inputId, label, language: field.language, children: /* @__PURE__ */ jsx(
36
+ return /* @__PURE__ */ jsx(PickerFrame, { inputId: field.inputId, label, required, language: field.language, children: /* @__PURE__ */ jsx(
35
37
  MonthInputPicker,
36
38
  {
37
39
  ...pickerProps,
38
40
  views: MONTH_VIEWS,
39
41
  openTo: "month",
40
- format: field.formats.month,
42
+ format: format ?? pickerProps?.format ?? field.formats.month,
41
43
  value: pickerMonthOf(value),
42
44
  minDate: pickerMonthOf(minMonth) ?? void 0,
43
45
  maxDate: pickerMonthOf(maxMonth) ?? void 0,
@@ -51,7 +53,7 @@ function MonthInputContent(props) {
51
53
  onBlur?.();
52
54
  },
53
55
  slotProps: textFieldSlotPropsOf(
54
- { id: field.inputId, name, onBlur, error: Boolean(field.errorText), helperText: field.errorText ?? helperText },
56
+ { id: field.inputId, name, onBlur, required, error: Boolean(field.errorText), helperText: field.errorText ?? helperText },
55
57
  pickerProps
56
58
  )
57
59
  }