@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.
- package/README.md +73 -6
- package/cli/calcita.js +4 -2
- package/cli/checkTexts.js +66 -9
- package/dist/appShell/BarActionButton.js.map +1 -1
- package/dist/datePickers/DateInput.js +5 -3
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +6 -4
- package/dist/datePickers/DateTimeInput.js.map +1 -1
- package/dist/datePickers/FormDateField.js +1 -1
- package/dist/datePickers/FormDateField.js.map +1 -1
- package/dist/datePickers/FormDateTimeField.js +1 -1
- package/dist/datePickers/FormDateTimeField.js.map +1 -1
- package/dist/datePickers/FormMonthField.js +1 -1
- package/dist/datePickers/FormMonthField.js.map +1 -1
- package/dist/datePickers/FormTimeField.js +1 -1
- package/dist/datePickers/FormTimeField.js.map +1 -1
- package/dist/datePickers/MonthInput.js +5 -3
- package/dist/datePickers/MonthInput.js.map +1 -1
- package/dist/datePickers/PickerFrame.js +2 -2
- package/dist/datePickers/PickerFrame.js.map +1 -1
- package/dist/datePickers/TimeInput.js +6 -4
- package/dist/datePickers/TimeInput.js.map +1 -1
- package/dist/feedback/Toaster.js +11 -2
- package/dist/feedback/Toaster.js.map +1 -1
- package/dist/forms/CalcitaFormik.js +11 -0
- package/dist/forms/CalcitaFormik.js.map +1 -0
- package/dist/forms/FormAttachmentsField.js +1 -1
- package/dist/forms/FormAttachmentsField.js.map +1 -1
- package/dist/forms/FormCatalogField.js +1 -1
- package/dist/forms/FormCatalogField.js.map +1 -1
- package/dist/forms/FormCheckboxGroupField.js +1 -1
- package/dist/forms/FormCheckboxGroupField.js.map +1 -1
- package/dist/forms/FormColorField.js +1 -1
- package/dist/forms/FormColorField.js.map +1 -1
- package/dist/forms/FormFileField.js +1 -1
- package/dist/forms/FormFileField.js.map +1 -1
- package/dist/forms/FormFileUploadField.js +1 -1
- package/dist/forms/FormFileUploadField.js.map +1 -1
- package/dist/forms/FormImageField.js +1 -1
- package/dist/forms/FormImageField.js.map +1 -1
- package/dist/forms/FormMaskedField.js +1 -1
- package/dist/forms/FormMaskedField.js.map +1 -1
- package/dist/forms/FormNumberField.js +1 -1
- package/dist/forms/FormNumberField.js.map +1 -1
- package/dist/forms/FormSchemaProvider.js +17 -0
- package/dist/forms/FormSchemaProvider.js.map +1 -0
- package/dist/forms/FormSelectField.js +1 -1
- package/dist/forms/FormSelectField.js.map +1 -1
- package/dist/forms/FormTextField.js +1 -1
- package/dist/forms/FormTextField.js.map +1 -1
- package/dist/forms/FormTransferListField.js +1 -1
- package/dist/forms/FormTransferListField.js.map +1 -1
- package/dist/forms/FormWizard.js +23 -6
- package/dist/forms/FormWizard.js.map +1 -1
- package/dist/forms/schemaRequired.js +33 -0
- package/dist/forms/schemaRequired.js.map +1 -0
- package/dist/forms/useFormikField.js +18 -5
- package/dist/forms/useFormikField.js.map +1 -1
- package/dist/forms.js +52 -46
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormAttachmentsField.js +1 -1
- package/dist/hookForm/FormAttachmentsField.js.map +1 -1
- package/dist/hookForm/FormCatalogField.js +1 -1
- package/dist/hookForm/FormCatalogField.js.map +1 -1
- package/dist/hookForm/FormCheckboxGroupField.js +1 -1
- package/dist/hookForm/FormCheckboxGroupField.js.map +1 -1
- package/dist/hookForm/FormColorField.js +1 -1
- package/dist/hookForm/FormColorField.js.map +1 -1
- package/dist/hookForm/FormFileField.js +1 -1
- package/dist/hookForm/FormFileField.js.map +1 -1
- package/dist/hookForm/FormFileUploadField.js +1 -1
- package/dist/hookForm/FormFileUploadField.js.map +1 -1
- package/dist/hookForm/FormImageField.js +1 -1
- package/dist/hookForm/FormImageField.js.map +1 -1
- package/dist/hookForm/FormMaskedField.js +1 -1
- package/dist/hookForm/FormMaskedField.js.map +1 -1
- package/dist/hookForm/FormNumberField.js +1 -1
- package/dist/hookForm/FormNumberField.js.map +1 -1
- package/dist/hookForm/FormSelectField.js +1 -1
- package/dist/hookForm/FormSelectField.js.map +1 -1
- package/dist/hookForm/FormTextField.js +1 -1
- package/dist/hookForm/FormTextField.js.map +1 -1
- package/dist/hookForm/FormTransferListField.js +1 -1
- package/dist/hookForm/FormTransferListField.js.map +1 -1
- package/dist/hookForm/FormWizard.js +21 -5
- package/dist/hookForm/FormWizard.js.map +1 -1
- package/dist/hookForm/useHookFormField.js +9 -3
- package/dist/hookForm/useHookFormField.js.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +3 -2
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +2 -1
- package/dist/inputs/CheckboxGroupInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +3 -1
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FieldFrame.js +18 -3
- package/dist/inputs/FieldFrame.js.map +1 -1
- package/dist/inputs/FormSection.js +42 -11
- package/dist/inputs/FormSection.js.map +1 -1
- package/dist/inputs/NumberInput.js +3 -1
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/SelectInput.js +3 -1
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +1 -1
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/TransferListInput.js +2 -1
- package/dist/inputs/TransferListInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +51 -17
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/inputs/formLayoutContext.js +7 -2
- package/dist/inputs/formLayoutContext.js.map +1 -1
- package/dist/inputs/requiredMark.js +17 -0
- package/dist/inputs/requiredMark.js.map +1 -0
- package/dist/inputs/useWizardSteps.js +36 -0
- package/dist/inputs/useWizardSteps.js.map +1 -0
- package/dist/inputs/wizardSteps.js +32 -0
- package/dist/inputs/wizardSteps.js.map +1 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/richText/FormRichTextField.js +1 -1
- package/dist/richText/FormRichTextField.js.map +1 -1
- package/dist/richText/RichTextEditor.js +4 -3
- package/dist/richText/RichTextEditor.js.map +1 -1
- package/dist/toolkit/ToolkitProvider.js +3 -0
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/toolkitContext.js.map +1 -1
- package/dist/types/appShell/BarActionButton.d.ts +1 -0
- package/dist/types/datePickers/DateInput.d.ts +2 -0
- package/dist/types/datePickers/DateTimeInput.d.ts +2 -0
- package/dist/types/datePickers/FormDateField.d.ts +3 -3
- package/dist/types/datePickers/FormDateTimeField.d.ts +3 -3
- package/dist/types/datePickers/FormMonthField.d.ts +3 -3
- package/dist/types/datePickers/FormTimeField.d.ts +3 -3
- package/dist/types/datePickers/MonthInput.d.ts +2 -0
- package/dist/types/datePickers/PickerFrame.d.ts +3 -1
- package/dist/types/datePickers/TimeInput.d.ts +2 -0
- package/dist/types/feedback/Toaster.d.ts +15 -1
- package/dist/types/feedback/index.d.ts +1 -1
- package/dist/types/forms/CalcitaFormik.d.ts +6 -0
- package/dist/types/forms/FormAttachmentsField.d.ts +3 -3
- package/dist/types/forms/FormCatalogField.d.ts +2 -2
- package/dist/types/forms/FormCheckboxGroupField.d.ts +3 -3
- package/dist/types/forms/FormColorField.d.ts +2 -2
- package/dist/types/forms/FormFileField.d.ts +3 -3
- package/dist/types/forms/FormFileUploadField.d.ts +3 -3
- package/dist/types/forms/FormImageField.d.ts +3 -3
- package/dist/types/forms/FormMaskedField.d.ts +2 -2
- package/dist/types/forms/FormNumberField.d.ts +2 -2
- package/dist/types/forms/FormSchemaProvider.d.ts +13 -0
- package/dist/types/forms/FormSelectField.d.ts +2 -2
- package/dist/types/forms/FormTextField.d.ts +2 -2
- package/dist/types/forms/FormTransferListField.d.ts +3 -3
- package/dist/types/forms/FormWizard.d.ts +14 -1
- package/dist/types/forms/index.d.ts +1 -0
- package/dist/types/forms/schemaRequired.d.ts +14 -0
- package/dist/types/forms/useFormikField.d.ts +10 -4
- package/dist/types/forms.d.ts +4 -2
- package/dist/types/hookForm/FormAttachmentsField.d.ts +3 -3
- package/dist/types/hookForm/FormCatalogField.d.ts +3 -3
- package/dist/types/hookForm/FormCheckboxGroupField.d.ts +3 -3
- package/dist/types/hookForm/FormColorField.d.ts +3 -3
- package/dist/types/hookForm/FormFileField.d.ts +3 -3
- package/dist/types/hookForm/FormFileUploadField.d.ts +3 -3
- package/dist/types/hookForm/FormImageField.d.ts +3 -3
- package/dist/types/hookForm/FormMaskedField.d.ts +3 -3
- package/dist/types/hookForm/FormNumberField.d.ts +3 -3
- package/dist/types/hookForm/FormSelectField.d.ts +3 -3
- package/dist/types/hookForm/FormTextField.d.ts +3 -3
- package/dist/types/hookForm/FormTransferListField.d.ts +3 -3
- package/dist/types/hookForm/FormWizard.d.ts +14 -1
- package/dist/types/hookForm/useHookFormField.d.ts +8 -4
- package/dist/types/index.d.ts +4 -3
- package/dist/types/inputs/CatalogInput.d.ts +1 -0
- package/dist/types/inputs/CheckboxGroupInput.d.ts +1 -0
- package/dist/types/inputs/ColorInput.d.ts +1 -0
- package/dist/types/inputs/FieldFrame.d.ts +1 -0
- package/dist/types/inputs/FormSection.d.ts +13 -0
- package/dist/types/inputs/NumberInput.d.ts +1 -0
- package/dist/types/inputs/SelectInput.d.ts +1 -0
- package/dist/types/inputs/TextInput.d.ts +1 -1
- package/dist/types/inputs/TransferListInput.d.ts +1 -0
- package/dist/types/inputs/WizardLayout.d.ts +6 -2
- package/dist/types/inputs/formLayoutContext.d.ts +8 -1
- package/dist/types/inputs/requiredMark.d.ts +9 -0
- package/dist/types/inputs/useWizardSteps.d.ts +23 -0
- package/dist/types/inputs/wizardSteps.d.ts +65 -0
- package/dist/types/locales/en.d.ts +1 -0
- package/dist/types/locales/es.d.ts +1 -0
- package/dist/types/richText/FormRichTextField.d.ts +3 -3
- package/dist/types/richText/RichTextEditor.d.ts +1 -0
- package/dist/types/toolkit/ToolkitProvider.d.ts +5 -1
- package/dist/types/toolkit/toolkitContext.d.ts +2 -1
- package/dist/types/ui/cards/RecordCard.d.ts +12 -0
- package/dist/types/ui/index.d.ts +1 -1
- package/dist/types/ui/navigation/BackButton.d.ts +1 -0
- package/dist/types/upload/AttachmentsInput.d.ts +2 -0
- package/dist/types/upload/FileInput.d.ts +1 -0
- package/dist/types/upload/FileUploadInput.d.ts +1 -0
- package/dist/types/upload/ImageInput.d.ts +1 -0
- package/dist/ui/cards/RecordCard.js +6 -2
- package/dist/ui/cards/RecordCard.js.map +1 -1
- package/dist/ui/navigation/BackButton.js.map +1 -1
- package/dist/upload/AttachmentsInput.js +2 -1
- package/dist/upload/AttachmentsInput.js.map +1 -1
- package/dist/upload/FileInput.js +2 -1
- package/dist/upload/FileInput.js.map +1 -1
- package/dist/upload/FileUploadInput.js +2 -1
- package/dist/upload/FileUploadInput.js.map +1 -1
- package/dist/upload/ImageInput.js +2 -1
- package/dist/upload/ImageInput.js.map +1 -1
- 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
|
|
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
|
|
987
|
-
|
|
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
|
|
12
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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;
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|