@stackcraftmx/calcita 0.52.0 → 0.54.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 (211) hide show
  1. package/README.md +109 -4
  2. package/dist/datePickers/DateInput.js +5 -3
  3. package/dist/datePickers/DateInput.js.map +1 -1
  4. package/dist/datePickers/DateTimeInput.js +6 -4
  5. package/dist/datePickers/DateTimeInput.js.map +1 -1
  6. package/dist/datePickers/FormDateField.js +1 -1
  7. package/dist/datePickers/FormDateField.js.map +1 -1
  8. package/dist/datePickers/FormDateTimeField.js +1 -1
  9. package/dist/datePickers/FormDateTimeField.js.map +1 -1
  10. package/dist/datePickers/FormMonthField.js +1 -1
  11. package/dist/datePickers/FormMonthField.js.map +1 -1
  12. package/dist/datePickers/FormTimeField.js +1 -1
  13. package/dist/datePickers/FormTimeField.js.map +1 -1
  14. package/dist/datePickers/MonthInput.js +5 -3
  15. package/dist/datePickers/MonthInput.js.map +1 -1
  16. package/dist/datePickers/PickerFrame.js +2 -2
  17. package/dist/datePickers/PickerFrame.js.map +1 -1
  18. package/dist/datePickers/TimeInput.js +6 -4
  19. package/dist/datePickers/TimeInput.js.map +1 -1
  20. package/dist/feedback/Toaster.js +11 -2
  21. package/dist/feedback/Toaster.js.map +1 -1
  22. package/dist/filepond/FileDropzone.js +60 -19
  23. package/dist/filepond/FileDropzone.js.map +1 -1
  24. package/dist/filepond/pondServer.js +86 -27
  25. package/dist/filepond/pondServer.js.map +1 -1
  26. package/dist/forms/CalcitaFormik.js +11 -0
  27. package/dist/forms/CalcitaFormik.js.map +1 -0
  28. package/dist/forms/FormAttachmentsField.js +1 -1
  29. package/dist/forms/FormAttachmentsField.js.map +1 -1
  30. package/dist/forms/FormCatalogField.js +1 -1
  31. package/dist/forms/FormCatalogField.js.map +1 -1
  32. package/dist/forms/FormCheckboxGroupField.js +1 -1
  33. package/dist/forms/FormCheckboxGroupField.js.map +1 -1
  34. package/dist/forms/FormColorField.js +1 -1
  35. package/dist/forms/FormColorField.js.map +1 -1
  36. package/dist/forms/FormFileField.js +1 -1
  37. package/dist/forms/FormFileField.js.map +1 -1
  38. package/dist/forms/FormFileUploadField.js +1 -1
  39. package/dist/forms/FormFileUploadField.js.map +1 -1
  40. package/dist/forms/FormImageField.js +1 -1
  41. package/dist/forms/FormImageField.js.map +1 -1
  42. package/dist/forms/FormMaskedField.js +1 -1
  43. package/dist/forms/FormMaskedField.js.map +1 -1
  44. package/dist/forms/FormNumberField.js +1 -1
  45. package/dist/forms/FormNumberField.js.map +1 -1
  46. package/dist/forms/FormSchemaProvider.js +17 -0
  47. package/dist/forms/FormSchemaProvider.js.map +1 -0
  48. package/dist/forms/FormSelectField.js +1 -1
  49. package/dist/forms/FormSelectField.js.map +1 -1
  50. package/dist/forms/FormTextField.js +1 -1
  51. package/dist/forms/FormTextField.js.map +1 -1
  52. package/dist/forms/FormTransferListField.js +1 -1
  53. package/dist/forms/FormTransferListField.js.map +1 -1
  54. package/dist/forms/FormWizard.js +2 -0
  55. package/dist/forms/FormWizard.js.map +1 -1
  56. package/dist/forms/schemaRequired.js +33 -0
  57. package/dist/forms/schemaRequired.js.map +1 -0
  58. package/dist/forms/useFormikField.js +18 -5
  59. package/dist/forms/useFormikField.js.map +1 -1
  60. package/dist/forms.js +52 -46
  61. package/dist/forms.js.map +1 -1
  62. package/dist/hookForm/FormAttachmentsField.js +1 -1
  63. package/dist/hookForm/FormAttachmentsField.js.map +1 -1
  64. package/dist/hookForm/FormCatalogField.js +1 -1
  65. package/dist/hookForm/FormCatalogField.js.map +1 -1
  66. package/dist/hookForm/FormCheckboxGroupField.js +1 -1
  67. package/dist/hookForm/FormCheckboxGroupField.js.map +1 -1
  68. package/dist/hookForm/FormColorField.js +1 -1
  69. package/dist/hookForm/FormColorField.js.map +1 -1
  70. package/dist/hookForm/FormFileField.js +1 -1
  71. package/dist/hookForm/FormFileField.js.map +1 -1
  72. package/dist/hookForm/FormFileUploadField.js +1 -1
  73. package/dist/hookForm/FormFileUploadField.js.map +1 -1
  74. package/dist/hookForm/FormImageField.js +1 -1
  75. package/dist/hookForm/FormImageField.js.map +1 -1
  76. package/dist/hookForm/FormMaskedField.js +1 -1
  77. package/dist/hookForm/FormMaskedField.js.map +1 -1
  78. package/dist/hookForm/FormNumberField.js +1 -1
  79. package/dist/hookForm/FormNumberField.js.map +1 -1
  80. package/dist/hookForm/FormSelectField.js +1 -1
  81. package/dist/hookForm/FormSelectField.js.map +1 -1
  82. package/dist/hookForm/FormTextField.js +1 -1
  83. package/dist/hookForm/FormTextField.js.map +1 -1
  84. package/dist/hookForm/FormTransferListField.js +1 -1
  85. package/dist/hookForm/FormTransferListField.js.map +1 -1
  86. package/dist/hookForm/FormWizard.js +2 -0
  87. package/dist/hookForm/FormWizard.js.map +1 -1
  88. package/dist/hookForm/useHookFormField.js +9 -3
  89. package/dist/hookForm/useHookFormField.js.map +1 -1
  90. package/dist/index.js +6 -2
  91. package/dist/index.js.map +1 -1
  92. package/dist/inputs/CatalogInput.js +3 -2
  93. package/dist/inputs/CatalogInput.js.map +1 -1
  94. package/dist/inputs/CheckboxGroupInput.js +2 -1
  95. package/dist/inputs/CheckboxGroupInput.js.map +1 -1
  96. package/dist/inputs/ColorInput.js +3 -1
  97. package/dist/inputs/ColorInput.js.map +1 -1
  98. package/dist/inputs/FieldFrame.js +18 -3
  99. package/dist/inputs/FieldFrame.js.map +1 -1
  100. package/dist/inputs/FormSection.js +42 -11
  101. package/dist/inputs/FormSection.js.map +1 -1
  102. package/dist/inputs/NumberInput.js +3 -1
  103. package/dist/inputs/NumberInput.js.map +1 -1
  104. package/dist/inputs/SelectInput.js +3 -1
  105. package/dist/inputs/SelectInput.js.map +1 -1
  106. package/dist/inputs/TextInput.js +1 -1
  107. package/dist/inputs/TextInput.js.map +1 -1
  108. package/dist/inputs/TransferListInput.js +2 -1
  109. package/dist/inputs/TransferListInput.js.map +1 -1
  110. package/dist/inputs/WizardLayout.js +12 -6
  111. package/dist/inputs/WizardLayout.js.map +1 -1
  112. package/dist/inputs/formLayoutContext.js +7 -2
  113. package/dist/inputs/formLayoutContext.js.map +1 -1
  114. package/dist/inputs/requiredMark.js +17 -0
  115. package/dist/inputs/requiredMark.js.map +1 -0
  116. package/dist/locales/en.js +2 -0
  117. package/dist/locales/en.js.map +1 -1
  118. package/dist/locales/es.js +2 -0
  119. package/dist/locales/es.js.map +1 -1
  120. package/dist/richText/FormRichTextField.js +1 -1
  121. package/dist/richText/FormRichTextField.js.map +1 -1
  122. package/dist/richText/RichTextEditor.js +4 -3
  123. package/dist/richText/RichTextEditor.js.map +1 -1
  124. package/dist/toolkit/ToolkitProvider.js +3 -0
  125. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  126. package/dist/toolkit/toolkitContext.js.map +1 -1
  127. package/dist/types/datePickers/DateInput.d.ts +2 -0
  128. package/dist/types/datePickers/DateTimeInput.d.ts +2 -0
  129. package/dist/types/datePickers/FormDateField.d.ts +3 -3
  130. package/dist/types/datePickers/FormDateTimeField.d.ts +3 -3
  131. package/dist/types/datePickers/FormMonthField.d.ts +3 -3
  132. package/dist/types/datePickers/FormTimeField.d.ts +3 -3
  133. package/dist/types/datePickers/MonthInput.d.ts +2 -0
  134. package/dist/types/datePickers/PickerFrame.d.ts +3 -1
  135. package/dist/types/datePickers/TimeInput.d.ts +2 -0
  136. package/dist/types/feedback/Toaster.d.ts +15 -1
  137. package/dist/types/feedback/index.d.ts +1 -1
  138. package/dist/types/filepond/FileDropzone.d.ts +5 -0
  139. package/dist/types/filepond/chunkedUpload.d.ts +122 -0
  140. package/dist/types/filepond/pondServer.d.ts +25 -3
  141. package/dist/types/forms/CalcitaFormik.d.ts +6 -0
  142. package/dist/types/forms/FormAttachmentsField.d.ts +3 -3
  143. package/dist/types/forms/FormCatalogField.d.ts +2 -2
  144. package/dist/types/forms/FormCheckboxGroupField.d.ts +3 -3
  145. package/dist/types/forms/FormColorField.d.ts +2 -2
  146. package/dist/types/forms/FormFileField.d.ts +3 -3
  147. package/dist/types/forms/FormFileUploadField.d.ts +3 -3
  148. package/dist/types/forms/FormImageField.d.ts +3 -3
  149. package/dist/types/forms/FormMaskedField.d.ts +2 -2
  150. package/dist/types/forms/FormNumberField.d.ts +2 -2
  151. package/dist/types/forms/FormSchemaProvider.d.ts +13 -0
  152. package/dist/types/forms/FormSelectField.d.ts +2 -2
  153. package/dist/types/forms/FormTextField.d.ts +2 -2
  154. package/dist/types/forms/FormTransferListField.d.ts +3 -3
  155. package/dist/types/forms/FormWizard.d.ts +3 -1
  156. package/dist/types/forms/index.d.ts +1 -0
  157. package/dist/types/forms/schemaRequired.d.ts +14 -0
  158. package/dist/types/forms/useFormikField.d.ts +10 -4
  159. package/dist/types/forms.d.ts +4 -2
  160. package/dist/types/hookForm/FormAttachmentsField.d.ts +3 -3
  161. package/dist/types/hookForm/FormCatalogField.d.ts +3 -3
  162. package/dist/types/hookForm/FormCheckboxGroupField.d.ts +3 -3
  163. package/dist/types/hookForm/FormColorField.d.ts +3 -3
  164. package/dist/types/hookForm/FormFileField.d.ts +3 -3
  165. package/dist/types/hookForm/FormFileUploadField.d.ts +3 -3
  166. package/dist/types/hookForm/FormImageField.d.ts +3 -3
  167. package/dist/types/hookForm/FormMaskedField.d.ts +3 -3
  168. package/dist/types/hookForm/FormNumberField.d.ts +3 -3
  169. package/dist/types/hookForm/FormSelectField.d.ts +3 -3
  170. package/dist/types/hookForm/FormTextField.d.ts +3 -3
  171. package/dist/types/hookForm/FormTransferListField.d.ts +3 -3
  172. package/dist/types/hookForm/FormWizard.d.ts +3 -1
  173. package/dist/types/hookForm/useHookFormField.d.ts +8 -4
  174. package/dist/types/index.d.ts +3 -2
  175. package/dist/types/inputs/CatalogInput.d.ts +1 -0
  176. package/dist/types/inputs/CheckboxGroupInput.d.ts +1 -0
  177. package/dist/types/inputs/ColorInput.d.ts +1 -0
  178. package/dist/types/inputs/FieldFrame.d.ts +1 -0
  179. package/dist/types/inputs/FormSection.d.ts +13 -0
  180. package/dist/types/inputs/NumberInput.d.ts +1 -0
  181. package/dist/types/inputs/SelectInput.d.ts +1 -0
  182. package/dist/types/inputs/TextInput.d.ts +1 -1
  183. package/dist/types/inputs/TransferListInput.d.ts +1 -0
  184. package/dist/types/inputs/WizardLayout.d.ts +1 -0
  185. package/dist/types/inputs/formLayoutContext.d.ts +8 -1
  186. package/dist/types/inputs/requiredMark.d.ts +9 -0
  187. package/dist/types/locales/en.d.ts +2 -0
  188. package/dist/types/locales/es.d.ts +2 -0
  189. package/dist/types/richText/FormRichTextField.d.ts +3 -3
  190. package/dist/types/richText/RichTextEditor.d.ts +1 -0
  191. package/dist/types/toolkit/ToolkitProvider.d.ts +5 -1
  192. package/dist/types/toolkit/toolkitContext.d.ts +2 -1
  193. package/dist/types/upload/AttachmentsInput.d.ts +2 -0
  194. package/dist/types/upload/FileInput.d.ts +1 -0
  195. package/dist/types/upload/FileUploadInput.d.ts +1 -0
  196. package/dist/types/upload/ImageInput.d.ts +1 -0
  197. package/dist/types/upload/chunkedUpload.d.ts +189 -0
  198. package/dist/types/upload/uploadWithProgress.d.ts +4 -2
  199. package/dist/upload/AttachmentsInput.js +2 -1
  200. package/dist/upload/AttachmentsInput.js.map +1 -1
  201. package/dist/upload/FileInput.js +2 -1
  202. package/dist/upload/FileInput.js.map +1 -1
  203. package/dist/upload/FileUploadInput.js +2 -1
  204. package/dist/upload/FileUploadInput.js.map +1 -1
  205. package/dist/upload/ImageInput.js +2 -1
  206. package/dist/upload/ImageInput.js.map +1 -1
  207. package/dist/upload/chunkedUpload.js +261 -0
  208. package/dist/upload/chunkedUpload.js.map +1 -0
  209. package/dist/upload/uploadWithProgress.js +8 -3
  210. package/dist/upload/uploadWithProgress.js.map +1 -1
  211. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { useHookFormField } from "./useHookFormField.js";
4
4
  import ImageInput from "../upload/ImageInput.js";
5
5
  function FormImageField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(ImageInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormImageField.js","sources":["../../src/hookForm/FormImageField.jsx"],"sourcesContent":["// Campo de una imagen de react-hook-form: ImageInput (upload/) conectado a su campo con useController. Se ve y se porta igual\n// que el FormImageField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport ImageInput from '@/upload/ImageInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/upload/ImageInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las del campo de la casa, sin\n * value / onChange / onBlur / error; control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormImageField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <ImageInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,eAAe,EAAC,MAAM,SAAS,GAAG,cAAa;AACnE,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,YAAA,EAAY,GAAG,YAAa,GAAG,WAAA,CAAY;AACvD;"}
1
+ {"version":3,"file":"FormImageField.js","sources":["../../src/hookForm/FormImageField.jsx"],"sourcesContent":["// Campo de una imagen de react-hook-form: ImageInput (upload/) conectado a su campo con useController. Se ve y se porta igual\n// que el FormImageField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport ImageInput from '@/upload/ImageInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/upload/ImageInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/upload/ImageInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las del campo de la casa, sin\n * value ni error (su onChange y su onBlur corren después de los del formulario); control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormImageField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <ImageInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,eAAe,EAAC,MAAM,SAAS,GAAG,cAAa;AACnE,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,YAAA,EAAY,GAAG,YAAa,GAAG,WAAA,CAAY;AACvD;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { useHookFormField } from "./useHookFormField.js";
4
4
  import MaskedInput from "../inputs/MaskedInput.js";
5
5
  function FormMaskedField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(MaskedInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormMaskedField.js","sources":["../../src/hookForm/FormMaskedField.jsx"],"sourcesContent":["// Campo con máscara de react-hook-form: MaskedInput (inputs/) conectado a su campo. Al formulario llega el valor LIMPIO (sin\n// separadores, en mayúsculas): «5551234567», no «555 123 4567».\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport MaskedInput from '@/inputs/MaskedInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/MaskedInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props\n * las de MaskedInput (mask, label, helperText, placeholder…), sin value / onChange / onBlur / error; control: el de useForm,\n * si la pantalla no usa FormProvider\n */\nexport default function FormMaskedField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <MaskedInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,gBAAgB,EAAC,MAAM,SAAS,GAAG,cAAa;AACpE,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,aAAA,EAAa,GAAG,YAAa,GAAG,WAAA,CAAY;AACxD;"}
1
+ {"version":3,"file":"FormMaskedField.js","sources":["../../src/hookForm/FormMaskedField.jsx"],"sourcesContent":["// Campo con máscara de react-hook-form: MaskedInput (inputs/) conectado a su campo. Al formulario llega el valor LIMPIO (sin\n// separadores, en mayúsculas): «5551234567», no «555 123 4567».\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport MaskedInput from '@/inputs/MaskedInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/MaskedInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/inputs/MaskedInput.jsx').default>[0], 'onChange'|'onBlur'>>} props\n * las de MaskedInput (mask, label, helperText, placeholder…), sin value ni error (su onChange y su onBlur corren después de los del formulario); control: el de useForm,\n * si la pantalla no usa FormProvider\n */\nexport default function FormMaskedField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <MaskedInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAYA,SAAwB,gBAAgB,EAAC,MAAM,SAAS,GAAG,cAAa;AACpE,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,aAAA,EAAa,GAAG,YAAa,GAAG,WAAA,CAAY;AACxD;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import NumberInput from "../inputs/NumberInput.js";
4
4
  import { useHookFormField } from "./useHookFormField.js";
5
5
  function FormNumberField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(NumberInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormNumberField.js","sources":["../../src/hookForm/FormNumberField.jsx"],"sourcesContent":["// Campo de número de react-hook-form: NumberInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormNumberField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport NumberInput from '@/inputs/NumberInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/NumberInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las del campo de la casa, sin\n * value / onChange / onBlur / error; control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormNumberField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <NumberInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,gBAAgB,EAAC,MAAM,SAAS,GAAG,cAAa;AACpE,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,aAAA,EAAa,GAAG,YAAa,GAAG,WAAA,CAAY;AACxD;"}
1
+ {"version":3,"file":"FormNumberField.js","sources":["../../src/hookForm/FormNumberField.jsx"],"sourcesContent":["// Campo de número de react-hook-form: NumberInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormNumberField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport NumberInput from '@/inputs/NumberInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/NumberInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/inputs/NumberInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las del campo de la casa, sin\n * value ni error (su onChange y su onBlur corren después de los del formulario); control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormNumberField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <NumberInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,gBAAgB,EAAC,MAAM,SAAS,GAAG,cAAa;AACpE,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,aAAA,EAAa,GAAG,YAAa,GAAG,WAAA,CAAY;AACxD;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import SelectInput from "../inputs/SelectInput.js";
4
4
  import { useHookFormField } from "./useHookFormField.js";
5
5
  function FormSelectField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(SelectInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormSelectField.js","sources":["../../src/hookForm/FormSelectField.jsx"],"sourcesContent":["// Campo de lista desplegable de react-hook-form: SelectInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormSelectField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport SelectInput from '@/inputs/SelectInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/SelectInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las del campo de la casa, sin\n * value / onChange / onBlur / error; control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormSelectField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <SelectInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,gBAAgB,EAAC,MAAM,SAAS,GAAG,cAAa;AACpE,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,aAAA,EAAa,GAAG,YAAa,GAAG,WAAA,CAAY;AACxD;"}
1
+ {"version":3,"file":"FormSelectField.js","sources":["../../src/hookForm/FormSelectField.jsx"],"sourcesContent":["// Campo de lista desplegable de react-hook-form: SelectInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormSelectField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport SelectInput from '@/inputs/SelectInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/SelectInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/inputs/SelectInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las del campo de la casa, sin\n * value ni error (su onChange y su onBlur corren después de los del formulario); control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormSelectField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <SelectInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,gBAAgB,EAAC,MAAM,SAAS,GAAG,cAAa;AACpE,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,aAAA,EAAa,GAAG,YAAa,GAAG,WAAA,CAAY;AACxD;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import TextInput from "../inputs/TextInput.js";
4
4
  import { useHookFormField } from "./useHookFormField.js";
5
5
  function FormTextField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(TextInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormTextField.js","sources":["../../src/hookForm/FormTextField.jsx"],"sourcesContent":["// Campo de texto de react-hook-form: TextInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormTextField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/TextInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las del campo de la casa, sin\n * value / onChange / onBlur / error; control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormTextField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <TextInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,cAAc,EAAC,MAAM,SAAS,GAAG,cAAa;AAClE,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,WAAA,EAAW,GAAG,YAAa,GAAG,WAAA,CAAY;AACtD;"}
1
+ {"version":3,"file":"FormTextField.js","sources":["../../src/hookForm/FormTextField.jsx"],"sourcesContent":["// Campo de texto de react-hook-form: TextInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormTextField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/TextInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/inputs/TextInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las del campo de la casa, sin\n * value ni error (su onChange y su onBlur corren después de los del formulario); control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormTextField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <TextInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,cAAc,EAAC,MAAM,SAAS,GAAG,cAAa;AAClE,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,WAAA,EAAW,GAAG,YAAa,GAAG,WAAA,CAAY;AACtD;"}
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { useHookFormField } from "./useHookFormField.js";
4
4
  import TransferListInput from "../inputs/TransferListInput.js";
5
5
  function FormTransferListField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(TransferListInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormTransferListField.js","sources":["../../src/hookForm/FormTransferListField.jsx"],"sourcesContent":["// La lista de transferencia de react-hook-form: TransferListInput (inputs/) conectada con useController. Se ve y se porta\n// igual que la de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport TransferListInput from '@/inputs/TransferListInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/TransferListInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las del campo de la casa, sin\n * value / onChange / onBlur / error; control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormTransferListField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <TransferListInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,sBAAsB,EAAC,MAAM,SAAS,GAAG,cAAa;AAC1E,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,mBAAA,EAAmB,GAAG,YAAa,GAAG,WAAA,CAAY;AAC9D;"}
1
+ {"version":3,"file":"FormTransferListField.js","sources":["../../src/hookForm/FormTransferListField.jsx"],"sourcesContent":["// La lista de transferencia de react-hook-form: TransferListInput (inputs/) conectada con useController. Se ve y se porta\n// igual que la de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport TransferListInput from '@/inputs/TransferListInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/TransferListInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/inputs/TransferListInput.jsx').default>[0], 'onChange'|'onBlur'>>} props las del campo de la casa, sin\n * value ni error (su onChange y su onBlur corren después de los del formulario); control: el de useForm, si la pantalla no usa FormProvider\n */\nexport default function FormTransferListField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <TransferListInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,sBAAsB,EAAC,MAAM,SAAS,GAAG,cAAa;AAC1E,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,mBAAA,EAAmB,GAAG,YAAa,GAAG,WAAA,CAAY;AAC9D;"}
@@ -11,6 +11,7 @@ function FormWizard({
11
11
  steps,
12
12
  submitLabel,
13
13
  orientation,
14
+ hideBackOnFirstStep,
14
15
  initialStepKey,
15
16
  stepSelection,
16
17
  onStepChange,
@@ -59,6 +60,7 @@ function FormWizard({
59
60
  skippedKeys,
60
61
  isSubmitting,
61
62
  orientation,
63
+ hideBackOnFirstStep,
62
64
  onBack: () => setActiveIndex((currentIndex) => currentIndex - 1),
63
65
  onNext: goNext,
64
66
  onSkip: skipStep,
@@ -1 +1 @@
1
- {"version":3,"file":"FormWizard.js","sources":["../../src/hookForm/FormWizard.jsx"],"sourcesContent":["// Formulario por pasos (wizard) con react-hook-form: el mismo del de formik (@stackcraftmx/calcita/forms), con las mismas\n// props y el mismo comportamiento:\n// - «Siguiente» revisa SOLO los campos del paso (trigger con sus nombres) y no avanza si alguno tiene error;\n// - «Omitir este paso» (solo en los opcionales) regresa sus campos a su valor de inicio (resetField): omitir es no dar ese\n// dato, no dejar lo que se escribió a medias;\n// - el último paso envía TODO el formulario (el esquema de yup completo vale para todos los pasos);\n// - si el envío falla por un campo de un paso anterior (yup o el 422 del servidor con applyServerProblem), regresa solo a\n// ese paso: un error que no se ve no se puede corregir;\n// - para editar un registro (se entra a corregir un dato suelto): initialStepKey abre en el paso que diga el servidor,\n// orientation=\"vertical\" pone los campos dentro de su paso, y stepSelection deja pulsar el título de otro paso para\n// saltar directo (free) o solo si el paso abierto no tiene errores (checked). onStepChange avisa cada cambio de paso.\n//\n// Va DENTRO de <FormProvider> y del <form> con su handleSubmit (usa useFormContext):\n// const form = useForm({resolver: yupResolver(schema), defaultValues});\n// <FormProvider {...form}>\n// <form noValidate onSubmit={form.handleSubmit(send)}>\n// <FormWizard submitLabel=\"Enviar solicitud\" steps={[\n// {key: 'datos', label: 'Sus datos', fields: ['name', 'email'], content: <>…campos…</>},\n// {key: 'aval', label: 'Aval', optional: true, fields: ['guarantor'], content: <>…</>},\n// ]} />\n// </form>\n// </FormProvider>\n//\n// Lo que se ve vive en WizardLayout (inputs/). Los textos se cambian con `texts`: {wizard: {next: 'Continuar'}}.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useRef, useState} from 'react';\nimport {useFormContext} from 'react-hook-form';\n\nimport FormProblemAlert from '@/hookForm/FormProblemAlert.jsx';\nimport SubmitButton from '@/hookForm/SubmitButton.jsx';\nimport {useWizardSteps} from '@/inputs/useWizardSteps.js';\nimport WizardLayout from '@/inputs/WizardLayout.jsx';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {string[]} [props.steps[].fields] los campos del paso (los que revisa «Siguiente»)\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {boolean} [props.steps[].completed] lleva la palomita (cuando los títulos se pulsan, solo si la pantalla lo dice)\n * @param {string} props.submitLabel ya traducido: el botón del último paso\n * @param {'horizontal'|'vertical'} [props.orientation] uno de WIZARD_ORIENTATION; por omisión horizontal\n * @param {string} [props.initialStepKey] la llave del paso en el que se abre (la que diga el servidor); por omisión el primero\n * @param {'none'|'free'|'checked'} [props.stepSelection] uno de WIZARD_STEP_SELECTION: si el título de otro paso se pulsa y qué\n * revisa antes; por omisión none (se avanza con «Siguiente», como siempre)\n * @param {(stepKey: string, index: number) => void} [props.onStepChange] cada vez que cambia el paso abierto\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function FormWizard({\n steps, submitLabel, orientation, initialStepKey, stepSelection, onStepChange, texts, ...unknownProps\n}) {\n warnUnknownProps('FormWizard', unknownProps);\n const {trigger, resetField, getFieldState, formState} = useFormContext();\n const {isSubmitting, submitCount} = formState;\n const [skippedKeys, setSkippedKeys] = useState(() => new Set());\n const wasSubmitting = useRef(false);\n\n // revisa SOLO los campos de un paso (trigger los marca, los pinta y pone el foco en el primero con error)\n const isStepValid = async (index) => {\n const stepFields = steps[index].fields ?? [];\n return stepFields.length === 0 || trigger(stepFields, {shouldFocus: true});\n };\n const {activeIndex, setActiveIndex, selectStep} = useWizardSteps({steps, initialStepKey, stepSelection, onStepChange, isStepValid});\n const activeStep = steps[activeIndex];\n\n // al terminar un envío fallido, regresa al paso con el primer error (solo en ese momento: escribir en otro paso no\n // debe jalar a la persona de regreso). OJO: getFieldState necesita formState para leer los errores de ahora\n useEffect(() => {\n if (wasSubmitting.current && !isSubmitting) {\n const errorStepIndex = steps.findIndex((step) => (step.fields ?? []).some((field) => getFieldState(field, formState).invalid));\n if (errorStepIndex >= 0) {\n setActiveIndex(errorStepIndex);\n }\n }\n wasSubmitting.current = isSubmitting;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isSubmitting, submitCount]);\n\n const goNext = async () => {\n // el paso tiene errores: se queda ahí para que se corrijan\n if (!(await isStepValid(activeIndex))) {\n return;\n }\n setSkippedKeys((currentKeys) => new Set([...currentKeys].filter((key) => key !== activeStep.key)));\n setActiveIndex((currentIndex) => currentIndex + 1);\n };\n\n const skipStep = () => {\n for (const field of activeStep.fields ?? []) {\n resetField(field);\n }\n setSkippedKeys((currentKeys) => new Set([...currentKeys, activeStep.key]));\n setActiveIndex((currentIndex) => currentIndex + 1);\n };\n\n return (\n <WizardLayout steps={steps} activeIndex={activeIndex} skippedKeys={skippedKeys} isSubmitting={isSubmitting} orientation={orientation}\n onBack={() => setActiveIndex((currentIndex) => currentIndex - 1)} onNext={goNext} onSkip={skipStep} onStepSelect={selectStep}\n submitButton={<SubmitButton>{submitLabel}</SubmitButton>} problemAlert={<FormProblemAlert />} texts={texts} />\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAoDA,SAAwB,WAAW;AAAA,EAC/B;AAAA,EAAO;AAAA,EAAa;AAAA,EAAa;AAAA,EAAgB;AAAA,EAAe;AAAA,EAAc;AAAA,EAAO,GAAG;AAC5F,GAAG;AACC,mBAAiB,cAAc,YAAY;AAC3C,QAAM,EAAC,SAAS,YAAY,eAAe,UAAA,IAAa,eAAA;AACxD,QAAM,EAAC,cAAc,YAAA,IAAe;AACpC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,MAAM,oBAAI,KAAK;AAC9D,QAAM,gBAAgB,OAAO,KAAK;AAGlC,QAAM,cAAc,OAAO,UAAU;AACjC,UAAM,aAAa,MAAM,KAAK,EAAE,UAAU,CAAA;AAC1C,WAAO,WAAW,WAAW,KAAK,QAAQ,YAAY,EAAC,aAAa,MAAK;AAAA,EAC7E;AACA,QAAM,EAAC,aAAa,gBAAgB,WAAA,IAAc,eAAe,EAAC,OAAO,gBAAgB,eAAe,cAAc,YAAA,CAAY;AAClI,QAAM,aAAa,MAAM,WAAW;AAIpC,YAAU,MAAM;AACZ,QAAI,cAAc,WAAW,CAAC,cAAc;AACxC,YAAM,iBAAiB,MAAM,UAAU,CAAC,UAAU,KAAK,UAAU,CAAA,GAAI,KAAK,CAAC,UAAU,cAAc,OAAO,SAAS,EAAE,OAAO,CAAC;AAC7H,UAAI,kBAAkB,GAAG;AACrB,uBAAe,cAAc;AAAA,MACjC;AAAA,IACJ;AACA,kBAAc,UAAU;AAAA,EAE5B,GAAG,CAAC,cAAc,WAAW,CAAC;AAE9B,QAAM,SAAS,YAAY;AAEvB,QAAI,CAAE,MAAM,YAAY,WAAW,GAAI;AACnC;AAAA,IACJ;AACA,mBAAe,CAAC,gBAAgB,IAAI,IAAI,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,QAAQ,QAAQ,WAAW,GAAG,CAAC,CAAC;AACjG,mBAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,EACrD;AAEA,QAAM,WAAW,MAAM;AACnB,eAAW,SAAS,WAAW,UAAU,CAAA,GAAI;AACzC,iBAAW,KAAK;AAAA,IACpB;AACA,mBAAe,CAAC,gBAAgB,oBAAI,IAAI,CAAC,GAAG,aAAa,WAAW,GAAG,CAAC,CAAC;AACzE,mBAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,EACrD;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAa;AAAA,MAAc;AAAA,MAA0B;AAAA,MAA0B;AAAA,MAA4B;AAAA,MAC9F,QAAQ,MAAM,eAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAQ,QAAQ;AAAA,MAAU,cAAc;AAAA,MAClH,cAAc,oBAAC,cAAA,EAAc,UAAA,YAAA,CAAY;AAAA,MAAiB,kCAAe,kBAAA,EAAiB;AAAA,MAAI;AAAA,IAAA;AAAA,EAAA;AAEpH;"}
1
+ {"version":3,"file":"FormWizard.js","sources":["../../src/hookForm/FormWizard.jsx"],"sourcesContent":["// Formulario por pasos (wizard) con react-hook-form: el mismo del de formik (@stackcraftmx/calcita/forms), con las mismas\n// props y el mismo comportamiento:\n// - «Siguiente» revisa SOLO los campos del paso (trigger con sus nombres) y no avanza si alguno tiene error;\n// - «Omitir este paso» (solo en los opcionales) regresa sus campos a su valor de inicio (resetField): omitir es no dar ese\n// dato, no dejar lo que se escribió a medias;\n// - el último paso envía TODO el formulario (el esquema de yup completo vale para todos los pasos);\n// - si el envío falla por un campo de un paso anterior (yup o el 422 del servidor con applyServerProblem), regresa solo a\n// ese paso: un error que no se ve no se puede corregir;\n// - para editar un registro (se entra a corregir un dato suelto): initialStepKey abre en el paso que diga el servidor,\n// orientation=\"vertical\" pone los campos dentro de su paso, y stepSelection deja pulsar el título de otro paso para\n// saltar directo (free) o solo si el paso abierto no tiene errores (checked). onStepChange avisa cada cambio de paso.\n//\n// Va DENTRO de <FormProvider> y del <form> con su handleSubmit (usa useFormContext):\n// const form = useForm({resolver: yupResolver(schema), defaultValues});\n// <FormProvider {...form}>\n// <form noValidate onSubmit={form.handleSubmit(send)}>\n// <FormWizard submitLabel=\"Enviar solicitud\" steps={[\n// {key: 'datos', label: 'Sus datos', fields: ['name', 'email'], content: <>…campos…</>},\n// {key: 'aval', label: 'Aval', optional: true, fields: ['guarantor'], content: <>…</>},\n// ]} />\n// </form>\n// </FormProvider>\n//\n// Lo que se ve vive en WizardLayout (inputs/). Los textos se cambian con `texts`: {wizard: {next: 'Continuar'}}.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useRef, useState} from 'react';\nimport {useFormContext} from 'react-hook-form';\n\nimport FormProblemAlert from '@/hookForm/FormProblemAlert.jsx';\nimport SubmitButton from '@/hookForm/SubmitButton.jsx';\nimport {useWizardSteps} from '@/inputs/useWizardSteps.js';\nimport WizardLayout from '@/inputs/WizardLayout.jsx';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {string[]} [props.steps[].fields] los campos del paso (los que revisa «Siguiente»)\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {boolean} [props.steps[].completed] lleva la palomita (cuando los títulos se pulsan, solo si la pantalla lo dice)\n * @param {string} props.submitLabel ya traducido: el botón del último paso\n * @param {'horizontal'|'vertical'} [props.orientation] uno de WIZARD_ORIENTATION; por omisión horizontal\n * @param {boolean} [props.hideBackOnFirstStep] en el primer paso no se pinta «Anterior»; por omisión sale apagado, como antes\n * @param {string} [props.initialStepKey] la llave del paso en el que se abre (la que diga el servidor); por omisión el primero\n * @param {'none'|'free'|'checked'} [props.stepSelection] uno de WIZARD_STEP_SELECTION: si el título de otro paso se pulsa y qué\n * revisa antes; por omisión none (se avanza con «Siguiente», como siempre)\n * @param {(stepKey: string, index: number) => void} [props.onStepChange] cada vez que cambia el paso abierto\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function FormWizard({\n steps, submitLabel, orientation, hideBackOnFirstStep, initialStepKey, stepSelection, onStepChange, texts, ...unknownProps\n}) {\n warnUnknownProps('FormWizard', unknownProps);\n const {trigger, resetField, getFieldState, formState} = useFormContext();\n const {isSubmitting, submitCount} = formState;\n const [skippedKeys, setSkippedKeys] = useState(() => new Set());\n const wasSubmitting = useRef(false);\n\n // revisa SOLO los campos de un paso (trigger los marca, los pinta y pone el foco en el primero con error)\n const isStepValid = async (index) => {\n const stepFields = steps[index].fields ?? [];\n return stepFields.length === 0 || trigger(stepFields, {shouldFocus: true});\n };\n const {activeIndex, setActiveIndex, selectStep} = useWizardSteps({steps, initialStepKey, stepSelection, onStepChange, isStepValid});\n const activeStep = steps[activeIndex];\n\n // al terminar un envío fallido, regresa al paso con el primer error (solo en ese momento: escribir en otro paso no\n // debe jalar a la persona de regreso). OJO: getFieldState necesita formState para leer los errores de ahora\n useEffect(() => {\n if (wasSubmitting.current && !isSubmitting) {\n const errorStepIndex = steps.findIndex((step) => (step.fields ?? []).some((field) => getFieldState(field, formState).invalid));\n if (errorStepIndex >= 0) {\n setActiveIndex(errorStepIndex);\n }\n }\n wasSubmitting.current = isSubmitting;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isSubmitting, submitCount]);\n\n const goNext = async () => {\n // el paso tiene errores: se queda ahí para que se corrijan\n if (!(await isStepValid(activeIndex))) {\n return;\n }\n setSkippedKeys((currentKeys) => new Set([...currentKeys].filter((key) => key !== activeStep.key)));\n setActiveIndex((currentIndex) => currentIndex + 1);\n };\n\n const skipStep = () => {\n for (const field of activeStep.fields ?? []) {\n resetField(field);\n }\n setSkippedKeys((currentKeys) => new Set([...currentKeys, activeStep.key]));\n setActiveIndex((currentIndex) => currentIndex + 1);\n };\n\n return (\n <WizardLayout steps={steps} activeIndex={activeIndex} skippedKeys={skippedKeys} isSubmitting={isSubmitting} orientation={orientation} hideBackOnFirstStep={hideBackOnFirstStep}\n onBack={() => setActiveIndex((currentIndex) => currentIndex - 1)} onNext={goNext} onSkip={skipStep} onStepSelect={selectStep}\n submitButton={<SubmitButton>{submitLabel}</SubmitButton>} problemAlert={<FormProblemAlert />} texts={texts} />\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAqDA,SAAwB,WAAW;AAAA,EAC/B;AAAA,EAAO;AAAA,EAAa;AAAA,EAAa;AAAA,EAAqB;AAAA,EAAgB;AAAA,EAAe;AAAA,EAAc;AAAA,EAAO,GAAG;AACjH,GAAG;AACC,mBAAiB,cAAc,YAAY;AAC3C,QAAM,EAAC,SAAS,YAAY,eAAe,UAAA,IAAa,eAAA;AACxD,QAAM,EAAC,cAAc,YAAA,IAAe;AACpC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,MAAM,oBAAI,KAAK;AAC9D,QAAM,gBAAgB,OAAO,KAAK;AAGlC,QAAM,cAAc,OAAO,UAAU;AACjC,UAAM,aAAa,MAAM,KAAK,EAAE,UAAU,CAAA;AAC1C,WAAO,WAAW,WAAW,KAAK,QAAQ,YAAY,EAAC,aAAa,MAAK;AAAA,EAC7E;AACA,QAAM,EAAC,aAAa,gBAAgB,WAAA,IAAc,eAAe,EAAC,OAAO,gBAAgB,eAAe,cAAc,YAAA,CAAY;AAClI,QAAM,aAAa,MAAM,WAAW;AAIpC,YAAU,MAAM;AACZ,QAAI,cAAc,WAAW,CAAC,cAAc;AACxC,YAAM,iBAAiB,MAAM,UAAU,CAAC,UAAU,KAAK,UAAU,CAAA,GAAI,KAAK,CAAC,UAAU,cAAc,OAAO,SAAS,EAAE,OAAO,CAAC;AAC7H,UAAI,kBAAkB,GAAG;AACrB,uBAAe,cAAc;AAAA,MACjC;AAAA,IACJ;AACA,kBAAc,UAAU;AAAA,EAE5B,GAAG,CAAC,cAAc,WAAW,CAAC;AAE9B,QAAM,SAAS,YAAY;AAEvB,QAAI,CAAE,MAAM,YAAY,WAAW,GAAI;AACnC;AAAA,IACJ;AACA,mBAAe,CAAC,gBAAgB,IAAI,IAAI,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,QAAQ,QAAQ,WAAW,GAAG,CAAC,CAAC;AACjG,mBAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,EACrD;AAEA,QAAM,WAAW,MAAM;AACnB,eAAW,SAAS,WAAW,UAAU,CAAA,GAAI;AACzC,iBAAW,KAAK;AAAA,IACpB;AACA,mBAAe,CAAC,gBAAgB,oBAAI,IAAI,CAAC,GAAG,aAAa,WAAW,GAAG,CAAC,CAAC;AACzE,mBAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,EACrD;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAa;AAAA,MAAc;AAAA,MAA0B;AAAA,MAA0B;AAAA,MAA4B;AAAA,MAA0B;AAAA,MACxH,QAAQ,MAAM,eAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAQ,QAAQ;AAAA,MAAU,cAAc;AAAA,MAClH,cAAc,oBAAC,cAAA,EAAc,UAAA,YAAA,CAAY;AAAA,MAAiB,kCAAe,kBAAA,EAAiB;AAAA,MAAI;AAAA,IAAA;AAAA,EAAA;AAEpH;"}
@@ -1,13 +1,19 @@
1
1
  "use client";
2
2
  import { useFormContext, useController } from "react-hook-form";
3
- function useHookFormField(name, control) {
3
+ function useHookFormField(name, control, screen = {}) {
4
4
  const formContext = useFormContext();
5
5
  const { field, fieldState } = useController({ name, control: control ?? formContext?.control });
6
6
  return {
7
7
  name,
8
8
  value: field.value,
9
- onChange: field.onChange,
10
- onBlur: field.onBlur,
9
+ onChange: (value, ...rest) => {
10
+ field.onChange(value);
11
+ screen.onChange?.(value, ...rest);
12
+ },
13
+ onBlur: (...args) => {
14
+ field.onBlur();
15
+ screen.onBlur?.(...args);
16
+ },
11
17
  // react-hook-form enfoca el primer campo con error al enviar: necesita el input de verdad
12
18
  inputRef: field.ref,
13
19
  error: fieldState.error?.message
@@ -1 +1 @@
1
- {"version":3,"file":"useHookFormField.js","sources":["../../src/hookForm/useHookFormField.js"],"sourcesContent":["// La conexión de un campo suelto (inputs/) con react-hook-form: lo que la vista necesita, sacado de useController.\n//\n// El campo se busca en el formulario de la pantalla (FormProvider); sin él, se le pasa control.\n//\n// Cuándo se enseña el error lo decide react-hook-form (por omisión, al intentar enviar y después al corregir): la vista\n// solo pinta el que llegue. Los errores de yup llegan como llave ({key, values}) y la vista los traduce.\n//\n// Hook genérico del paquete: function (regla de la casa).\nimport {useController, useFormContext} from 'react-hook-form';\n\n/**\n * @param {string} name el campo del formulario\n * @param {object} [control] el de useForm, si la pantalla no usa FormProvider\n * @returns {{name: string, value: unknown, onChange: (value: unknown) => void, onBlur: () => void, inputRef: Function, error: unknown}}\n */\nexport function useHookFormField(name, control) {\n const formContext = useFormContext();\n const {field, fieldState} = useController({name, control: control ?? formContext?.control});\n\n return {\n name,\n value: field.value,\n onChange: field.onChange,\n onBlur: field.onBlur,\n // react-hook-form enfoca el primer campo con error al enviar: necesita el input de verdad\n inputRef: field.ref,\n error: fieldState.error?.message,\n };\n}\n"],"names":[],"mappings":";;AAeO,SAAS,iBAAiB,MAAM,SAAS;AAC5C,QAAM,cAAc,eAAc;AAClC,QAAM,EAAC,OAAO,WAAU,IAAI,cAAc,EAAC,MAAM,SAAS,WAAW,aAAa,QAAO,CAAC;AAE1F,SAAO;AAAA,IACH;AAAA,IACA,OAAO,MAAM;AAAA,IACb,UAAU,MAAM;AAAA,IAChB,QAAQ,MAAM;AAAA;AAAA,IAEd,UAAU,MAAM;AAAA,IAChB,OAAO,WAAW,OAAO;AAAA,EACjC;AACA;"}
1
+ {"version":3,"file":"useHookFormField.js","sources":["../../src/hookForm/useHookFormField.js"],"sourcesContent":["// La conexión de un campo suelto (inputs/) con react-hook-form: lo que la vista necesita, sacado de useController.\n//\n// El campo se busca en el formulario de la pantalla (FormProvider); sin él, se le pasa control.\n//\n// Lo que la pantalla le pase al campo en onChange y onBlur también corre, después del de react-hook-form y con el valor\n// nuevo. OJO: antes el de react-hook-form iba después y lo pisaba sin avisar.\n//\n// Cuándo se enseña el error lo decide react-hook-form (por omisión, al intentar enviar y después al corregir): la vista\n// solo pinta el que llegue. Los errores de yup llegan como llave ({key, values}) y la vista los traduce.\n//\n// Hook genérico del paquete: function (regla de la casa).\nimport {useController, useFormContext} from 'react-hook-form';\n\n/**\n * @param {string} name el campo del formulario\n * @param {object} [control] el de useForm, si la pantalla no usa FormProvider\n * @param {{onChange?: Function, onBlur?: Function}} [screen] lo que la pantalla le dijo al campo\n * @returns {{name: string, value: unknown, onChange: (value: unknown, ...rest: unknown[]) => void, onBlur: (...args: unknown[]) => void, inputRef: Function, error: unknown}}\n */\nexport function useHookFormField(name, control, screen = {}) {\n const formContext = useFormContext();\n const {field, fieldState} = useController({name, control: control ?? formContext?.control});\n\n return {\n name,\n value: field.value,\n onChange: (value, ...rest) => {\n field.onChange(value);\n screen.onChange?.(value, ...rest);\n },\n onBlur: (...args) => {\n field.onBlur();\n screen.onBlur?.(...args);\n },\n // react-hook-form enfoca el primer campo con error al enviar: necesita el input de verdad\n inputRef: field.ref,\n error: fieldState.error?.message,\n };\n}\n"],"names":[],"mappings":";;AAmBO,SAAS,iBAAiB,MAAM,SAAS,SAAS,CAAA,GAAI;AACzD,QAAM,cAAc,eAAc;AAClC,QAAM,EAAC,OAAO,WAAU,IAAI,cAAc,EAAC,MAAM,SAAS,WAAW,aAAa,QAAO,CAAC;AAE1F,SAAO;AAAA,IACH;AAAA,IACA,OAAO,MAAM;AAAA,IACb,UAAU,CAAC,UAAU,SAAS;AAC1B,YAAM,SAAS,KAAK;AACpB,aAAO,WAAW,OAAO,GAAG,IAAI;AAAA,IACpC;AAAA,IACA,QAAQ,IAAI,SAAS;AACjB,YAAM,OAAM;AACZ,aAAO,SAAS,GAAG,IAAI;AAAA,IAC3B;AAAA;AAAA,IAEA,UAAU,MAAM;AAAA,IAChB,OAAO,WAAW,OAAO;AAAA,EACjC;AACA;"}
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { default as default5 } from "./feedback/ConfirmDialog.js";
14
14
  import { default as default6 } from "./feedback/ModalWaiting.js";
15
15
  import { NEW_BUILD_NOTICE_VARIANT, default as default7 } from "./feedback/NewBuildNotice.js";
16
16
  import { default as default8 } from "./feedback/StaleBuildBoundary.js";
17
- import { default as default9 } from "./feedback/Toaster.js";
17
+ import { TOASTER_POSITION, default as default9 } from "./feedback/Toaster.js";
18
18
  import { confirmActionMachine } from "./feedback/confirmActionMachine.js";
19
19
  import { TOAST_DEFAULT_OPTIONS, dismissToast, errorsObjectToMessage, toastDefault, toastError, toastInfo, toastLoading, toastProblem, toastPromise, toastSuccess, toastWarn } from "./feedback/toast.js";
20
20
  import { useConfirmAction } from "./feedback/useConfirmAction.js";
@@ -72,6 +72,7 @@ import { transferProgressOf } from "./upload/transferProgress.js";
72
72
  import { useFilePicker } from "./upload/useFilePicker.js";
73
73
  import { buildUploadActors, uploadMachine } from "./upload/uploadMachine.js";
74
74
  import { uploadWithProgress } from "./upload/uploadWithProgress.js";
75
+ import { createChunkedUpload } from "./upload/chunkedUpload.js";
75
76
  import { APP_BAR_HEIGHT_CSS_VARIABLE, SCROLL_MARGIN_BELOW_APP_BAR, useAppBarHeightPublisher } from "./ui/layout/appBarHeight.js";
76
77
  import { default as default30 } from "./ui/layout/CollapsibleSection.js";
77
78
  import { default as default31 } from "./ui/layout/ContentDialog.js";
@@ -120,7 +121,7 @@ import { default as default69 } from "./inputs/ColorInput.js";
120
121
  import { default as default70 } from "./inputs/FieldFrame.js";
121
122
  import { default as default71 } from "./inputs/MaskedInput.js";
122
123
  import { INPUT_MASK, isMaskComplete, maskPatternOf, maskedTextOf, rawValueOf } from "./inputs/inputMasks.js";
123
- import { FORM_ROW_CLASS, FormRow, default as default72 } from "./inputs/FormSection.js";
124
+ import { FORM_LABEL_PLACEMENT, FORM_ROW_CLASS, FormRow, default as default72 } from "./inputs/FormSection.js";
124
125
  import { default as default73 } from "./inputs/CheckboxGroupInput.js";
125
126
  import { default as default74 } from "./inputs/NumberInput.js";
126
127
  import { default as default75 } from "./inputs/TransferListInput.js";
@@ -236,6 +237,7 @@ export {
236
237
  FILTER_DRAWER_ANCHOR,
237
238
  FILTER_FIELD_KIND,
238
239
  FONT_SIZE_LEVELS,
240
+ FORM_LABEL_PLACEMENT,
239
241
  FORM_ROW_CLASS,
240
242
  default70 as FieldFrame,
241
243
  default25 as FileDropzone,
@@ -319,6 +321,7 @@ export {
319
321
  default8 as StaleBuildBoundary,
320
322
  default59 as StatusChip,
321
323
  THEME_LOOK,
324
+ TOASTER_POSITION,
322
325
  TOAST_DEFAULT_OPTIONS,
323
326
  TOP_BAR,
324
327
  TREND_DIRECTION,
@@ -365,6 +368,7 @@ export {
365
368
  createAccessChecker,
366
369
  createApiClient,
367
370
  createBrowserNavigation,
371
+ createChunkedUpload,
368
372
  createHashNavigation,
369
373
  createLogger,
370
374
  createPathIndex,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -30,6 +30,7 @@ function CatalogInputContent(props) {
30
30
  helperText = " ",
31
31
  loading = false,
32
32
  disabled = false,
33
+ required = false,
33
34
  inputRef,
34
35
  placeholder,
35
36
  multiple = false,
@@ -63,7 +64,7 @@ function CatalogInputContent(props) {
63
64
  return selectAllState.allChosen ? t("form.unselectAll") : t("form.selectAll", { count: matchingCodes.length });
64
65
  })();
65
66
  const showsSelectAll = multiple && selectAll;
66
- return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, children: /* @__PURE__ */ jsx(
67
+ return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, required, children: /* @__PURE__ */ jsx(
67
68
  CatalogInputField,
68
69
  {
69
70
  id: inputId,
@@ -132,7 +133,7 @@ function CatalogInputContent(props) {
132
133
  );
133
134
  } : void 0,
134
135
  onBlur,
135
- renderInput: (inputProps) => /* @__PURE__ */ jsx(TextField, { ...inputProps, name, inputRef, placeholder, error: Boolean(errorText), helperText: errorText ?? helperText })
136
+ renderInput: (inputProps) => /* @__PURE__ */ jsx(TextField, { ...inputProps, inputProps: { ...inputProps.inputProps, "aria-required": required || void 0 }, name, inputRef, placeholder, error: Boolean(errorText), helperText: errorText ?? helperText })
136
137
  }
137
138
  ) });
138
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogInput.js","sources":["../../src/inputs/CatalogInput.jsx"],"sourcesContent":["// Campo suelto que elige una clave de un catálogo ([{code, label}]): actividad económica, giro, país, banco… Se busca por la\n// clave o por el texto, sin acentos ni mayúsculas, y al formulario llega solo la clave. Con `multiple` elige varias: cada una\n// queda como chip con su ×, la lista no se cierra al elegir y al formulario llega la lista de claves en el orden elegido.\n// Con varias, además (cada una se pide):\n// selectAll «Elegir todos» arriba de la lista, con su casilla a medias si hay algunos; con algo escrito en el buscador,\n// «Elegir los N que coinciden» (todos los que coinciden, no solo los que se ven)\n// checkboxes una casilla en cada opción (se ve de un vistazo qué está elegido)\n// limitTags cuántos chips se ven con el campo sin foco; los demás, «+3 más»\n//\n// Por qué no un <select>: con catálogos de cientos de opciones no se puede usar; y con texto libre la gente inventa claves.\n// Se enseñan a lo más MAX_VISIBLE_OPTIONS coincidencias: con miles, la lista completa congela la pantalla.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaCatalogInput con defaultProps, styleOverrides de\n// sus partes (field) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Checkbox from '@mui/material/Checkbox';\nimport TextField from '@mui/material/TextField';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {selectAllStateOf} from '@/inputs/selectAllState.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.CalcitaCatalogInput)\nconst THEME_NAME = 'CalcitaCatalogInput';\nconst CatalogInputField = piecePart(THEME_NAME, 'Field', Autocomplete);\n\nconst MAX_VISIBLE_OPTIONS = 100;\n// la opción de «Elegir todos»: no es del catálogo y nunca llega al formulario\nconst SELECT_ALL_CODE = '__calcita_select_all__';\n\n/**\n * Un texto sin acentos ni mayúsculas, para que «credito» encuentre «Crédito».\n *\n * @param {unknown} text\n * @returns {string}\n */\nfunction plainTextOf(text) {\n return String(text ?? '').normalize('NFD').replace(/\\p{Diacritic}/gu, '').toLowerCase();\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|string[]|null|undefined} props.value la clave elegida ('' = ninguna); con multiple, la lista ([] = ninguna)\n * @param {(code: any) => void} props.onChange recibe la clave ('' al borrar); con multiple, la lista de claves\n * @param {() => void} [props.onBlur]\n * @param {Array<{code: string, label: string}>} props.options\n * @param {(option: {code: string, label: string}) => string} [props.formatLabel] por omisión «Nombre (clave)»\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {boolean} [props.loading] el catálogo todavía no llega\n * @param {string} [props.placeholder] la pista con el campo vacío, ya traducida (p. ej. «Busque un estado»): sin ella, en un\n * formulario agrupado el campo vacío solo enseña su botón y no se entiende qué es\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias claves\n * @param {boolean} [props.selectAll] con multiple: «Elegir todos» (o los que coinciden con lo buscado) arriba de la lista\n * @param {boolean} [props.checkboxes] con multiple: una casilla en cada opción\n * @param {number} [props.limitTags] con multiple: cuántos chips se ven sin foco; los demás como «+3 más»\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): 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 CatalogInputContent(props) {\n const {\n name, label, value, onChange, onBlur, options = [], formatLabel, error, helperText = ' ', loading = false, disabled = false, inputRef, placeholder,\n multiple = false, selectAll = false, checkboxes = false, limitTags, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const inputId = `${name ?? 'catalog'}-${generatedId}`;\n const errorText = validationTextOf(error);\n // con varias, en el orden en que se eligieron (el de la lista de claves)\n const selectedOption = multiple\n ? (Array.isArray(value) ? value : []).map((code) => options.find((option) => option.code === code)).filter(Boolean)\n : options.find((option) => option.code === value) ?? null;\n const optionLabelOf = (option) => (formatLabel ? formatLabel(option) : `${option.label} (${option.code})`);\n // lo escrito en el buscador: «Elegir todos» abarca lo que coincide con eso\n const [searchText, setSearchText] = useState('');\n const matchesSearch = (option, text) => {\n const wantedText = plainTextOf(text);\n return !wantedText || plainTextOf(optionLabelOf(option)).includes(wantedText);\n };\n const selectedCodes = multiple && Array.isArray(value) ? value : [];\n const matchingCodes = options.filter((option) => matchesSearch(option, searchText)).map((option) => option.code);\n const selectAllState = selectAllStateOf(selectedCodes, matchingCodes);\n const selectAllLabel = (() => {\n if (searchText.trim()) {\n return selectAllState.allChosen ? t('form.unselectMatching', {count: matchingCodes.length}) : t('form.selectMatching', {count: matchingCodes.length});\n }\n return selectAllState.allChosen ? t('form.unselectAll') : t('form.selectAll', {count: matchingCodes.length});\n })();\n const showsSelectAll = multiple && selectAll;\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <CatalogInputField\n id={inputId}\n multiple={multiple}\n disableCloseOnSelect={multiple}\n fullWidth\n size=\"small\"\n options={options}\n value={selectedOption}\n loading={loading}\n disabled={disabled}\n noOptionsText={t('form.noOptions')}\n loadingText={t('form.loadingOptions')}\n getOptionLabel={(option) => (option.code === SELECT_ALL_CODE ? selectAllLabel : optionLabelOf(option))}\n isOptionEqualToValue={(option, currentOption) => option.code === currentOption.code}\n filterOptions={(allOptions, {inputValue}) => {\n const matchingOptions = allOptions.filter((option) => matchesSearch(option, inputValue)).slice(0, MAX_VISIBLE_OPTIONS);\n // «Elegir todos» va arriba solo si hay de dónde elegir\n return showsSelectAll && matchingOptions.length > 0 ? [{code: SELECT_ALL_CODE, label: selectAllLabel}, ...matchingOptions] : matchingOptions;\n }}\n // con «Elegir todos», lo buscado se queda al elegir (MUI lo borraba): así se eligen varios de la misma búsqueda.\n // Al salir del campo sí se borra. Sin «Elegir todos», el campo se porta como siempre\n inputValue={showsSelectAll ? searchText : undefined}\n onInputChange={(event, text, reason) => {\n // MUI 7 avisa con qué borra el texto: al elegir o quitar una opción ('selectOption', 'removeOption', 'reset');\n // al salir del campo es 'blur' y ese sí se borra\n const isResetByChoosing = ['selectOption', 'removeOption', 'reset'].includes(reason) && event?.type !== 'blur';\n if (!(showsSelectAll && isResetByChoosing)) {\n setSearchText(text);\n }\n }}\n onChange={(event, option, reason, details) => {\n if (!multiple) {\n onChange(option?.code ?? '');\n return;\n }\n if (details?.option?.code === SELECT_ALL_CODE) {\n onChange(selectAllState.toggled());\n return;\n }\n onChange(option.filter((chosenOption) => chosenOption.code !== SELECT_ALL_CODE).map((chosenOption) => chosenOption.code));\n }}\n limitTags={multiple ? limitTags : undefined}\n getLimitTagsText={(hiddenCount) => t('form.moreSelected', {count: hiddenCount})}\n renderOption={multiple && (checkboxes || selectAll) ? (optionProps, option, {selected}) => {\n // OJO: key va aparte; dentro de las props React avisa en la consola\n const {key, ...liProps} = optionProps;\n const isSelectAll = option.code === SELECT_ALL_CODE;\n const showsBox = checkboxes || isSelectAll;\n return (\n <li key={key} {...liProps} {...(isSelectAll && {'aria-selected': selectAllState.allChosen})}\n style={isSelectAll ? {fontWeight: 600} : undefined}>\n {showsBox && (\n <Checkbox size=\"small\" tabIndex={-1} disableRipple sx={{p: 0, mr: 1}}\n checked={isSelectAll ? selectAllState.allChosen : selected}\n indeterminate={isSelectAll && selectAllState.someChosen} />\n )}\n {isSelectAll ? selectAllLabel : optionLabelOf(option)}\n </li>\n );\n } : undefined}\n onBlur={onBlur}\n renderInput={(inputProps) => (\n <TextField {...inputProps} name={name} inputRef={inputRef} placeholder={placeholder} error={Boolean(errorText)} helperText={errorText ?? helperText} />\n )}\n />\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst CatalogInput = withPieceSlots(CatalogInputContent, THEME_NAME);\nexport default CatalogInput;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CatalogInput.js","sources":["../../src/inputs/CatalogInput.jsx"],"sourcesContent":["// Campo suelto que elige una clave de un catálogo ([{code, label}]): actividad económica, giro, país, banco… Se busca por la\n// clave o por el texto, sin acentos ni mayúsculas, y al formulario llega solo la clave. Con `multiple` elige varias: cada una\n// queda como chip con su ×, la lista no se cierra al elegir y al formulario llega la lista de claves en el orden elegido.\n// Con varias, además (cada una se pide):\n// selectAll «Elegir todos» arriba de la lista, con su casilla a medias si hay algunos; con algo escrito en el buscador,\n// «Elegir los N que coinciden» (todos los que coinciden, no solo los que se ven)\n// checkboxes una casilla en cada opción (se ve de un vistazo qué está elegido)\n// limitTags cuántos chips se ven con el campo sin foco; los demás, «+3 más»\n//\n// Por qué no un <select>: con catálogos de cientos de opciones no se puede usar; y con texto libre la gente inventa claves.\n// Se enseñan a lo más MAX_VISIBLE_OPTIONS coincidencias: con miles, la lista completa congela la pantalla.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaCatalogInput con defaultProps, styleOverrides de\n// sus partes (field) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Autocomplete from '@mui/material/Autocomplete';\nimport Checkbox from '@mui/material/Checkbox';\nimport TextField from '@mui/material/TextField';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {selectAllStateOf} from '@/inputs/selectAllState.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.CalcitaCatalogInput)\nconst THEME_NAME = 'CalcitaCatalogInput';\nconst CatalogInputField = piecePart(THEME_NAME, 'Field', Autocomplete);\n\nconst MAX_VISIBLE_OPTIONS = 100;\n// la opción de «Elegir todos»: no es del catálogo y nunca llega al formulario\nconst SELECT_ALL_CODE = '__calcita_select_all__';\n\n/**\n * Un texto sin acentos ni mayúsculas, para que «credito» encuentre «Crédito».\n *\n * @param {unknown} text\n * @returns {string}\n */\nfunction plainTextOf(text) {\n return String(text ?? '').normalize('NFD').replace(/\\p{Diacritic}/gu, '').toLowerCase();\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|string[]|null|undefined} props.value la clave elegida ('' = ninguna); con multiple, la lista ([] = ninguna)\n * @param {(code: any) => void} props.onChange recibe la clave ('' al borrar); con multiple, la lista de claves\n * @param {() => void} [props.onBlur]\n * @param {Array<{code: string, label: string}>} props.options\n * @param {(option: {code: string, label: string}) => string} [props.formatLabel] por omisión «Nombre (clave)»\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {boolean} [props.loading] el catálogo todavía no llega\n * @param {string} [props.placeholder] la pista con el campo vacío, ya traducida (p. ej. «Busque un estado»): sin ella, en un\n * formulario agrupado el campo vacío solo enseña su botón y no se entiende qué es\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta y aria-required en el campo (sin required: con varias opciones elegidas el texto queda vacío y el navegador no dejaría enviar)\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias claves\n * @param {boolean} [props.selectAll] con multiple: «Elegir todos» (o los que coinciden con lo buscado) arriba de la lista\n * @param {boolean} [props.checkboxes] con multiple: una casilla en cada opción\n * @param {number} [props.limitTags] con multiple: cuántos chips se ven sin foco; los demás como «+3 más»\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): 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 CatalogInputContent(props) {\n const {\n name, label, value, onChange, onBlur, options = [], formatLabel, error, helperText = ' ', loading = false, disabled = false, required = false, inputRef, placeholder,\n multiple = false, selectAll = false, checkboxes = false, limitTags, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const inputId = `${name ?? 'catalog'}-${generatedId}`;\n const errorText = validationTextOf(error);\n // con varias, en el orden en que se eligieron (el de la lista de claves)\n const selectedOption = multiple\n ? (Array.isArray(value) ? value : []).map((code) => options.find((option) => option.code === code)).filter(Boolean)\n : options.find((option) => option.code === value) ?? null;\n const optionLabelOf = (option) => (formatLabel ? formatLabel(option) : `${option.label} (${option.code})`);\n // lo escrito en el buscador: «Elegir todos» abarca lo que coincide con eso\n const [searchText, setSearchText] = useState('');\n const matchesSearch = (option, text) => {\n const wantedText = plainTextOf(text);\n return !wantedText || plainTextOf(optionLabelOf(option)).includes(wantedText);\n };\n const selectedCodes = multiple && Array.isArray(value) ? value : [];\n const matchingCodes = options.filter((option) => matchesSearch(option, searchText)).map((option) => option.code);\n const selectAllState = selectAllStateOf(selectedCodes, matchingCodes);\n const selectAllLabel = (() => {\n if (searchText.trim()) {\n return selectAllState.allChosen ? t('form.unselectMatching', {count: matchingCodes.length}) : t('form.selectMatching', {count: matchingCodes.length});\n }\n return selectAllState.allChosen ? t('form.unselectAll') : t('form.selectAll', {count: matchingCodes.length});\n })();\n const showsSelectAll = multiple && selectAll;\n\n return (\n <FieldFrame inputId={inputId} label={label} required={required}>\n <CatalogInputField\n id={inputId}\n multiple={multiple}\n disableCloseOnSelect={multiple}\n fullWidth\n size=\"small\"\n options={options}\n value={selectedOption}\n loading={loading}\n disabled={disabled}\n noOptionsText={t('form.noOptions')}\n loadingText={t('form.loadingOptions')}\n getOptionLabel={(option) => (option.code === SELECT_ALL_CODE ? selectAllLabel : optionLabelOf(option))}\n isOptionEqualToValue={(option, currentOption) => option.code === currentOption.code}\n filterOptions={(allOptions, {inputValue}) => {\n const matchingOptions = allOptions.filter((option) => matchesSearch(option, inputValue)).slice(0, MAX_VISIBLE_OPTIONS);\n // «Elegir todos» va arriba solo si hay de dónde elegir\n return showsSelectAll && matchingOptions.length > 0 ? [{code: SELECT_ALL_CODE, label: selectAllLabel}, ...matchingOptions] : matchingOptions;\n }}\n // con «Elegir todos», lo buscado se queda al elegir (MUI lo borraba): así se eligen varios de la misma búsqueda.\n // Al salir del campo sí se borra. Sin «Elegir todos», el campo se porta como siempre\n inputValue={showsSelectAll ? searchText : undefined}\n onInputChange={(event, text, reason) => {\n // MUI 7 avisa con qué borra el texto: al elegir o quitar una opción ('selectOption', 'removeOption', 'reset');\n // al salir del campo es 'blur' y ese sí se borra\n const isResetByChoosing = ['selectOption', 'removeOption', 'reset'].includes(reason) && event?.type !== 'blur';\n if (!(showsSelectAll && isResetByChoosing)) {\n setSearchText(text);\n }\n }}\n onChange={(event, option, reason, details) => {\n if (!multiple) {\n onChange(option?.code ?? '');\n return;\n }\n if (details?.option?.code === SELECT_ALL_CODE) {\n onChange(selectAllState.toggled());\n return;\n }\n onChange(option.filter((chosenOption) => chosenOption.code !== SELECT_ALL_CODE).map((chosenOption) => chosenOption.code));\n }}\n limitTags={multiple ? limitTags : undefined}\n getLimitTagsText={(hiddenCount) => t('form.moreSelected', {count: hiddenCount})}\n renderOption={multiple && (checkboxes || selectAll) ? (optionProps, option, {selected}) => {\n // OJO: key va aparte; dentro de las props React avisa en la consola\n const {key, ...liProps} = optionProps;\n const isSelectAll = option.code === SELECT_ALL_CODE;\n const showsBox = checkboxes || isSelectAll;\n return (\n <li key={key} {...liProps} {...(isSelectAll && {'aria-selected': selectAllState.allChosen})}\n style={isSelectAll ? {fontWeight: 600} : undefined}>\n {showsBox && (\n <Checkbox size=\"small\" tabIndex={-1} disableRipple sx={{p: 0, mr: 1}}\n checked={isSelectAll ? selectAllState.allChosen : selected}\n indeterminate={isSelectAll && selectAllState.someChosen} />\n )}\n {isSelectAll ? selectAllLabel : optionLabelOf(option)}\n </li>\n );\n } : undefined}\n onBlur={onBlur}\n renderInput={(inputProps) => (\n <TextField {...inputProps} inputProps={{...inputProps.inputProps, 'aria-required': required || undefined}} name={name} inputRef={inputRef} placeholder={placeholder} error={Boolean(errorText)} helperText={errorText ?? helperText} />\n )}\n />\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst CatalogInput = withPieceSlots(CatalogInputContent, THEME_NAME);\nexport default CatalogInput;\n"],"names":[],"mappings":"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"}
@@ -30,6 +30,7 @@ function CheckboxGroupInputContent(props) {
30
30
  error,
31
31
  helperText = " ",
32
32
  disabled = false,
33
+ required = false,
33
34
  sx,
34
35
  messages,
35
36
  texts,
@@ -50,7 +51,7 @@ function CheckboxGroupInputContent(props) {
50
51
  onBlur?.();
51
52
  };
52
53
  const toggle = (optionValue) => change(checkedValues.includes(optionValue) ? checkedValues.filter((checkedValue) => checkedValue !== optionValue) : [...checkedValues, optionValue]);
53
- return /* @__PURE__ */ jsx(FieldFrame, { inputId: `${groupId}-first`, labelId, label, children: /* @__PURE__ */ jsxs("div", { children: [
54
+ return /* @__PURE__ */ jsx(FieldFrame, { inputId: `${groupId}-first`, labelId, label, required, children: /* @__PURE__ */ jsxs("div", { children: [
54
55
  /* @__PURE__ */ jsxs(CheckboxGroupRoot, { row, role: "group", "aria-labelledby": labelId, ownSx: { columnGap: 2 }, sx, children: [
55
56
  selectAll && /* @__PURE__ */ jsx(
56
57
  CheckboxGroupSelectAll,
@@ -1 +1 @@
1
- {"version":3,"file":"CheckboxGroupInput.js","sources":["../../src/inputs/CheckboxGroupInput.jsx"],"sourcesContent":["// El grupo de casillas: pocas opciones a la vista (los canales de aviso, los días que abre una sucursal), cada una con su\n// casilla y, si hace falta, una explicación abajo; al formulario llega la lista de las marcadas. Con pocas opciones es más\n// rápido que abrir una lista; con muchas, SelectInput o CatalogInput con `multiple`.\n//\n// selectAll pone «Elegir todos» arriba, con su casilla a medias si hay algunas marcadas. row las acomoda en renglón (en el\n// teléfono bajan solas). Dentro de una FormSection se vuelve renglón, como los demás campos.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaCheckboxGroupInput con defaultProps y\n// styleOverrides de sus partes: root, selectAll, option.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Checkbox from '@mui/material/Checkbox';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport FormGroup from '@mui/material/FormGroup';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {selectAllStateOf} from '@/inputs/selectAllState.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaCheckboxGroupInput)\nconst THEME_NAME = 'CalcitaCheckboxGroupInput';\nconst CheckboxGroupRoot = piecePart(THEME_NAME, 'Root', FormGroup);\nconst CheckboxGroupSelectAll = piecePart(THEME_NAME, 'SelectAll', FormControlLabel);\nconst CheckboxGroupOption = piecePart(THEME_NAME, 'Option', FormControlLabel);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {Array<string|number>|null|undefined} props.value las marcadas ([] = ninguna)\n * @param {(values: Array<string|number>) => void} props.onChange la lista nueva, en el orden de las opciones\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string, disabled?: boolean}>} props.options\n * @param {boolean} [props.row] en renglón en lugar de una debajo de otra\n * @param {boolean} [props.selectAll] «Elegir todos» arriba\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {boolean} [props.disabled]\n * @param {object} [props.sx]\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): 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 CheckboxGroupInputContent(props) {\n const {\n name, label, value, onChange, onBlur, options, row = false, selectAll = false, error, helperText = ' ', disabled = false, sx, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const groupId = `${name ?? 'checkbox-group'}-${generatedId}`;\n const labelId = `${groupId}-label`;\n const errorText = validationTextOf(error);\n const checkedValues = Array.isArray(value) ? value : [];\n const selectAllState = selectAllStateOf(checkedValues, options.filter((option) => !option.disabled).map((option) => option.value));\n // la lista en el orden de las opciones, no en el que se fueron marcando (así viaja siempre igual)\n const inOptionsOrder = (values) => options.map((option) => option.value).filter((optionValue) => values.includes(optionValue));\n const change = (values) => {\n onChange(inOptionsOrder(values));\n onBlur?.();\n };\n const toggle = (optionValue) => change(checkedValues.includes(optionValue)\n ? checkedValues.filter((checkedValue) => checkedValue !== optionValue)\n : [...checkedValues, optionValue]);\n\n return (\n <FieldFrame inputId={`${groupId}-first`} labelId={labelId} label={label}>\n <div>\n <CheckboxGroupRoot row={row} role=\"group\" aria-labelledby={labelId} ownSx={{columnGap: 2}} sx={sx}>\n {selectAll && (\n <CheckboxGroupSelectAll\n disabled={disabled}\n label={selectAllState.allChosen ? t('form.unselectAll') : t('form.selectAll', {count: options.length})}\n control={(\n <Checkbox size=\"small\" checked={selectAllState.allChosen} indeterminate={selectAllState.someChosen}\n onChange={() => change(selectAllState.toggled())} />\n )}\n ownSx={{'& .MuiFormControlLabel-label': {fontWeight: 600}}}\n />\n )}\n {options.map((option, index) => (\n <CheckboxGroupOption\n key={option.value}\n disabled={disabled || option.disabled}\n ownSx={{alignItems: option.description ? 'flex-start' : 'center'}}\n control={(\n <Checkbox size=\"small\" name={name} id={index === 0 ? `${groupId}-first` : undefined}\n checked={checkedValues.includes(option.value)} onChange={() => toggle(option.value)}\n sx={option.description ? {mt: -0.5} : undefined} />\n )}\n label={option.description ? (\n <Stack component=\"span\">\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n </Stack>\n ) : option.label}\n />\n ))}\n </CheckboxGroupRoot>\n <FormHelperText error={Boolean(errorText)}>{errorText ?? helperText}</FormHelperText>\n </div>\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst CheckboxGroupInput = withPieceSlots(CheckboxGroupInputContent, THEME_NAME);\nexport default CheckboxGroupInput;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CheckboxGroupInput.js","sources":["../../src/inputs/CheckboxGroupInput.jsx"],"sourcesContent":["// El grupo de casillas: pocas opciones a la vista (los canales de aviso, los días que abre una sucursal), cada una con su\n// casilla y, si hace falta, una explicación abajo; al formulario llega la lista de las marcadas. Con pocas opciones es más\n// rápido que abrir una lista; con muchas, SelectInput o CatalogInput con `multiple`.\n//\n// selectAll pone «Elegir todos» arriba, con su casilla a medias si hay algunas marcadas. row las acomoda en renglón (en el\n// teléfono bajan solas). Dentro de una FormSection se vuelve renglón, como los demás campos.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaCheckboxGroupInput con defaultProps y\n// styleOverrides de sus partes: root, selectAll, option.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Checkbox from '@mui/material/Checkbox';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport FormGroup from '@mui/material/FormGroup';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {selectAllStateOf} from '@/inputs/selectAllState.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaCheckboxGroupInput)\nconst THEME_NAME = 'CalcitaCheckboxGroupInput';\nconst CheckboxGroupRoot = piecePart(THEME_NAME, 'Root', FormGroup);\nconst CheckboxGroupSelectAll = piecePart(THEME_NAME, 'SelectAll', FormControlLabel);\nconst CheckboxGroupOption = piecePart(THEME_NAME, 'Option', FormControlLabel);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {Array<string|number>|null|undefined} props.value las marcadas ([] = ninguna)\n * @param {(values: Array<string|number>) => void} props.onChange la lista nueva, en el orden de las opciones\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string, disabled?: boolean}>} props.options\n * @param {boolean} [props.row] en renglón en lugar de una debajo de otra\n * @param {boolean} [props.selectAll] «Elegir todos» arriba\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta (lo revisa el esquema del formulario)\n * @param {object} [props.sx]\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): 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 CheckboxGroupInputContent(props) {\n const {\n name, label, value, onChange, onBlur, options, row = false, selectAll = false, error, helperText = ' ', disabled = false, required = false, sx, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const groupId = `${name ?? 'checkbox-group'}-${generatedId}`;\n const labelId = `${groupId}-label`;\n const errorText = validationTextOf(error);\n const checkedValues = Array.isArray(value) ? value : [];\n const selectAllState = selectAllStateOf(checkedValues, options.filter((option) => !option.disabled).map((option) => option.value));\n // la lista en el orden de las opciones, no en el que se fueron marcando (así viaja siempre igual)\n const inOptionsOrder = (values) => options.map((option) => option.value).filter((optionValue) => values.includes(optionValue));\n const change = (values) => {\n onChange(inOptionsOrder(values));\n onBlur?.();\n };\n const toggle = (optionValue) => change(checkedValues.includes(optionValue)\n ? checkedValues.filter((checkedValue) => checkedValue !== optionValue)\n : [...checkedValues, optionValue]);\n\n return (\n <FieldFrame inputId={`${groupId}-first`} labelId={labelId} label={label} required={required}>\n <div>\n <CheckboxGroupRoot row={row} role=\"group\" aria-labelledby={labelId} ownSx={{columnGap: 2}} sx={sx}>\n {selectAll && (\n <CheckboxGroupSelectAll\n disabled={disabled}\n label={selectAllState.allChosen ? t('form.unselectAll') : t('form.selectAll', {count: options.length})}\n control={(\n <Checkbox size=\"small\" checked={selectAllState.allChosen} indeterminate={selectAllState.someChosen}\n onChange={() => change(selectAllState.toggled())} />\n )}\n ownSx={{'& .MuiFormControlLabel-label': {fontWeight: 600}}}\n />\n )}\n {options.map((option, index) => (\n <CheckboxGroupOption\n key={option.value}\n disabled={disabled || option.disabled}\n ownSx={{alignItems: option.description ? 'flex-start' : 'center'}}\n control={(\n <Checkbox size=\"small\" name={name} id={index === 0 ? `${groupId}-first` : undefined}\n checked={checkedValues.includes(option.value)} onChange={() => toggle(option.value)}\n sx={option.description ? {mt: -0.5} : undefined} />\n )}\n label={option.description ? (\n <Stack component=\"span\">\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n </Stack>\n ) : option.label}\n />\n ))}\n </CheckboxGroupRoot>\n <FormHelperText error={Boolean(errorText)}>{errorText ?? helperText}</FormHelperText>\n </div>\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst CheckboxGroupInput = withPieceSlots(CheckboxGroupInputContent, THEME_NAME);\nexport default CheckboxGroupInput;\n"],"names":[],"mappings":"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"}
@@ -32,6 +32,7 @@ function ColorInputContent(props) {
32
32
  helperText = " ",
33
33
  checkContrast = true,
34
34
  disabled = false,
35
+ required = false,
35
36
  inputRef,
36
37
  messages,
37
38
  texts,
@@ -49,7 +50,7 @@ function ColorInputContent(props) {
49
50
  const errorText = validationTextOf(error);
50
51
  const contrastText = contrastRatio && checkContrast ? t("form.colorContrast", { ratio: contrastRatio.toFixed(RATIO_DECIMALS) }) : helperText;
51
52
  const choose = (nextColor) => onChange(nextColor.toUpperCase());
52
- return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, children: /* @__PURE__ */ jsxs(Stack, { spacing: 1, children: [
53
+ return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, required, children: /* @__PURE__ */ jsxs(Stack, { spacing: 1, children: [
53
54
  /* @__PURE__ */ jsx(
54
55
  TextField,
55
56
  {
@@ -60,6 +61,7 @@ function ColorInputContent(props) {
60
61
  fullWidth: true,
61
62
  value: color,
62
63
  disabled,
64
+ required,
63
65
  placeholder: "#RRGGBB",
64
66
  onChange: (event) => choose(event.target.value),
65
67
  onBlur,
@@ -1 +1 @@
1
- {"version":3,"file":"ColorInput.js","sources":["../../src/inputs/ColorInput.jsx"],"sourcesContent":["// Campo de COLOR suelto (viene de «Identidad de documentos» de PLD): la muestra redonda (el «color well» de Apple, que abre\n// el selector del sistema) dentro del campo del texto #RRGGBB, el contraste contra el blanco mientras se elige y, si el color casi no se leería, el mismo tono más oscuro con su botón\n// para usarlo.\n//\n// Para lo que se imprime o se lee sobre blanco (el color de una marca en un comprobante). El color llega al formulario en\n// mayúsculas (#2B3A67); '' = sin color.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaColorInput con defaultProps y\n// styleOverrides de sus partes: swatch, nativeInput.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport {HEX_COLOR, MIN_CONTRAST, contrastOnWhite, readableVariantOf} from '@/inputs/colorContrast.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaColorInput)\nconst THEME_NAME = 'CalcitaColorInput';\nconst ColorSwatch = piecePart(THEME_NAME, 'Swatch', Box);\nconst ColorNativeInput = piecePart(THEME_NAME, 'NativeInput', Box);\n\n// #RRGGBB\nconst COLOR_LENGTH = 7;\n// el color que enseña el selector del navegador mientras el texto no es un color\nconst NEUTRAL_COLOR = '#000000';\nconst RATIO_DECIMALS = 1;\n// la muestra redonda (px) y su orilla fina en claro y en oscuro\nconst SWATCH_PIXELS = 24;\nconst SWATCH_EDGE = Object.freeze({light: 'rgba(15, 23, 42, 0.18)', dark: 'rgba(255, 255, 255, 0.24)'});\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 '#RRGGBB' o ''\n * @param {(color: string) => void} props.onChange recibe el color en mayúsculas\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] cuando no hay color\n * @param {boolean} [props.checkContrast] avisa si casi no se lee sobre blanco (por omisión sí)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): 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 ColorInputContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaColorInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', checkContrast = true, disabled = false, inputRef, messages, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const inputId = `${name ?? 'color'}-${generatedId}`;\n const color = value ?? '';\n const isValidColor = HEX_COLOR.test(color);\n const contrastRatio = isValidColor ? contrastOnWhite(color) : null;\n const suggestion = checkContrast && isValidColor && contrastRatio < MIN_CONTRAST ? readableVariantOf(color) : null;\n const errorText = validationTextOf(error);\n const contrastText = contrastRatio && checkContrast ? t('form.colorContrast', {ratio: contrastRatio.toFixed(RATIO_DECIMALS)}) : helperText;\n const choose = (nextColor) => onChange(nextColor.toUpperCase());\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <Stack spacing={1}>\n {/* un solo campo: la muestra redonda al inicio (el «color well» de Apple) y el código al lado. Picar la muestra\n abre el selector de colores del sistema (un input type=color invisible encima de ella). Antes eran dos cajas\n sueltas (el selector del navegador en la suya y el código en otra) y se veían como piezas sueltas */}\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n size=\"small\"\n fullWidth\n value={color}\n disabled={disabled}\n placeholder=\"#RRGGBB\"\n onChange={(event) => choose(event.target.value)}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? contrastText}\n slotProps={{\n input: {\n startAdornment: (\n <InputAdornment position=\"start\">\n <ColorSwatch component=\"label\"\n ownSx={(theme) => ({\n position: 'relative', display: 'block', flexShrink: 0, width: SWATCH_PIXELS, height: SWATCH_PIXELS,\n borderRadius: '50%', cursor: disabled ? 'default' : 'pointer',\n backgroundColor: isValidColor ? color : 'transparent',\n // la orilla de la muestra: que se vea aunque el color sea el del fondo (blanco en claro, negro en oscuro)\n boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.light}`,\n ...theme.applyStyles('dark', {boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.dark}`}),\n })}>\n <ColorNativeInput component=\"input\" type=\"color\" value={isValidColor ? color : NEUTRAL_COLOR} disabled={disabled}\n aria-label={t('form.pickColor')} onChange={(event) => choose(event.target.value)}\n ownSx={{position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0, border: 0, p: 0, cursor: 'inherit'}} />\n </ColorSwatch>\n </InputAdornment>\n ),\n },\n htmlInput: {maxLength: COLOR_LENGTH, spellCheck: false, style: {fontVariantNumeric: 'tabular-nums'}},\n }}\n />\n {suggestion && (\n <Alert severity=\"warning\"\n action={<Button color=\"inherit\" size=\"small\" onClick={() => choose(suggestion)}>{t('form.colorUseSuggestion', {color: suggestion})}</Button>}>\n {t('form.colorLowContrast', {suggestion})}\n </Alert>\n )}\n </Stack>\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst ColorInput = withPieceSlots(ColorInputContent, THEME_NAME);\nexport default ColorInput;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ColorInput.js","sources":["../../src/inputs/ColorInput.jsx"],"sourcesContent":["// Campo de COLOR suelto (viene de «Identidad de documentos» de PLD): la muestra redonda (el «color well» de Apple, que abre\n// el selector del sistema) dentro del campo del texto #RRGGBB, el contraste contra el blanco mientras se elige y, si el color casi no se leería, el mismo tono más oscuro con su botón\n// para usarlo.\n//\n// Para lo que se imprime o se lee sobre blanco (el color de una marca en un comprobante). El color llega al formulario en\n// mayúsculas (#2B3A67); '' = sin color.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaColorInput con defaultProps y\n// styleOverrides de sus partes: swatch, nativeInput.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport {HEX_COLOR, MIN_CONTRAST, contrastOnWhite, readableVariantOf} from '@/inputs/colorContrast.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaColorInput)\nconst THEME_NAME = 'CalcitaColorInput';\nconst ColorSwatch = piecePart(THEME_NAME, 'Swatch', Box);\nconst ColorNativeInput = piecePart(THEME_NAME, 'NativeInput', Box);\n\n// #RRGGBB\nconst COLOR_LENGTH = 7;\n// el color que enseña el selector del navegador mientras el texto no es un color\nconst NEUTRAL_COLOR = '#000000';\nconst RATIO_DECIMALS = 1;\n// la muestra redonda (px) y su orilla fina en claro y en oscuro\nconst SWATCH_PIXELS = 24;\nconst SWATCH_EDGE = Object.freeze({light: 'rgba(15, 23, 42, 0.18)', dark: 'rgba(255, 255, 255, 0.24)'});\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 '#RRGGBB' o ''\n * @param {(color: string) => void} props.onChange recibe el color en mayúsculas\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] cuando no hay color\n * @param {boolean} [props.checkContrast] avisa si casi no se lee sobre blanco (por omisión sí)\n * @param {boolean} [props.disabled]\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 {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): 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 ColorInputContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaColorInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', checkContrast = true, disabled = false, required = false, inputRef, messages, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const inputId = `${name ?? 'color'}-${generatedId}`;\n const color = value ?? '';\n const isValidColor = HEX_COLOR.test(color);\n const contrastRatio = isValidColor ? contrastOnWhite(color) : null;\n const suggestion = checkContrast && isValidColor && contrastRatio < MIN_CONTRAST ? readableVariantOf(color) : null;\n const errorText = validationTextOf(error);\n const contrastText = contrastRatio && checkContrast ? t('form.colorContrast', {ratio: contrastRatio.toFixed(RATIO_DECIMALS)}) : helperText;\n const choose = (nextColor) => onChange(nextColor.toUpperCase());\n\n return (\n <FieldFrame inputId={inputId} label={label} required={required}>\n <Stack spacing={1}>\n {/* un solo campo: la muestra redonda al inicio (el «color well» de Apple) y el código al lado. Picar la muestra\n abre el selector de colores del sistema (un input type=color invisible encima de ella). Antes eran dos cajas\n sueltas (el selector del navegador en la suya y el código en otra) y se veían como piezas sueltas */}\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n size=\"small\"\n fullWidth\n value={color}\n disabled={disabled}\n required={required}\n placeholder=\"#RRGGBB\"\n onChange={(event) => choose(event.target.value)}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? contrastText}\n slotProps={{\n input: {\n startAdornment: (\n <InputAdornment position=\"start\">\n <ColorSwatch component=\"label\"\n ownSx={(theme) => ({\n position: 'relative', display: 'block', flexShrink: 0, width: SWATCH_PIXELS, height: SWATCH_PIXELS,\n borderRadius: '50%', cursor: disabled ? 'default' : 'pointer',\n backgroundColor: isValidColor ? color : 'transparent',\n // la orilla de la muestra: que se vea aunque el color sea el del fondo (blanco en claro, negro en oscuro)\n boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.light}`,\n ...theme.applyStyles('dark', {boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.dark}`}),\n })}>\n <ColorNativeInput component=\"input\" type=\"color\" value={isValidColor ? color : NEUTRAL_COLOR} disabled={disabled}\n aria-label={t('form.pickColor')} onChange={(event) => choose(event.target.value)}\n ownSx={{position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0, border: 0, p: 0, cursor: 'inherit'}} />\n </ColorSwatch>\n </InputAdornment>\n ),\n },\n htmlInput: {maxLength: COLOR_LENGTH, spellCheck: false, style: {fontVariantNumeric: 'tabular-nums'}},\n }}\n />\n {suggestion && (\n <Alert severity=\"warning\"\n action={<Button color=\"inherit\" size=\"small\" onClick={() => choose(suggestion)}>{t('form.colorUseSuggestion', {color: suggestion})}</Button>}>\n {t('form.colorLowContrast', {suggestion})}\n </Alert>\n )}\n </Stack>\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst ColorInput = withPieceSlots(ColorInputContent, THEME_NAME);\nexport default ColorInput;\n"],"names":[],"mappings":"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"}
@@ -4,20 +4,35 @@ import Stack from "@mui/material/Stack";
4
4
  import Typography from "@mui/material/Typography";
5
5
  import { FormRow } from "./FormSection.js";
6
6
  import { useIsInsideFormSection } from "./formLayoutContext.js";
7
+ import { requiredMarkSxOf, REQUIRED_LABEL_ATTRIBUTE } from "./requiredMark.js";
7
8
  import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
8
9
  import { warnUnknownProps } from "../toolkit/unknownProps.js";
10
+ import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
9
11
  const THEME_NAME = "CalcitaFieldFrame";
10
12
  const FieldFrameRoot = piecePart(THEME_NAME, "Root", Stack);
11
13
  const FieldFrameLabel = piecePart(THEME_NAME, "Label", Typography);
12
14
  function FieldFrameContent(props) {
13
- const { inputId, labelId, label, children, ...unknownProps } = usePieceProps(props, THEME_NAME);
15
+ const { inputId, labelId, label, required = false, children, ...unknownProps } = usePieceProps(props, THEME_NAME);
14
16
  warnUnknownProps(THEME_NAME, unknownProps);
15
17
  const isRow = useIsInsideFormSection();
18
+ const { t } = useToolkitTranslation("common");
16
19
  if (isRow) {
17
- return /* @__PURE__ */ jsx(FormRow, { label, htmlFor: inputId, labelId, children });
20
+ return /* @__PURE__ */ jsx(FormRow, { label, htmlFor: inputId, labelId, required, children });
18
21
  }
19
22
  return /* @__PURE__ */ jsxs(FieldFrameRoot, { spacing: 0.75, children: [
20
- /* @__PURE__ */ jsx(FieldFrameLabel, { component: "label", id: labelId, htmlFor: inputId, variant: "overline", color: "text.secondary", children: label }),
23
+ /* @__PURE__ */ jsx(
24
+ FieldFrameLabel,
25
+ {
26
+ component: "label",
27
+ id: labelId,
28
+ htmlFor: inputId,
29
+ variant: "overline",
30
+ color: "text.secondary",
31
+ ...required && { [REQUIRED_LABEL_ATTRIBUTE]: "true" },
32
+ ownSx: required ? requiredMarkSxOf(t("form.requiredMark")) : void 0,
33
+ children: label
34
+ }
35
+ ),
21
36
  children
22
37
  ] });
23
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FieldFrame.js","sources":["../../src/inputs/FieldFrame.jsx"],"sourcesContent":["// El marco de todos los campos del paquete. Fuera de una FormSection, la etiqueta va ARRIBA del campo, ligada a él por su\n// id; dentro de una FormSection (el formulario agrupado de Apple), el campo se vuelve un renglón: la etiqueta a la\n// izquierda y el valor a la derecha (FormRow).\n//\n// La etiqueta va afuera y no flotando adentro del campo: se lee igual con el campo vacío o lleno, y no brinca al escribir.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFieldFrame con styleOverrides de sus\n// partes root (la columna) y label (la etiqueta de arriba), y slots/slotProps para cambiarlas. Dentro de una FormSection el\n// renglón es CalcitaFormRow.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {FormRow} from '@/inputs/FormSection.jsx';\nimport {useIsInsideFormSection} from '@/inputs/formLayoutContext.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFieldFrame)\nconst THEME_NAME = 'CalcitaFieldFrame';\nconst FieldFrameRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FieldFrameLabel = piecePart(THEME_NAME, 'Label', Typography);\n\n/**\n * @param {object} props\n * @param {string} props.inputId el id del campo (la etiqueta lo nombra para el lector de pantalla)\n * @param {string} [props.labelId] el id de la etiqueta, para un campo que no es un <input> (la lista desplegable de MUI es un\n * div: `for` no la une con su etiqueta y el campo la nombra con aria-labelledby)\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {import('react').ReactNode} props.children el campo\n */\nfunction FieldFrameContent(props) {\n const {inputId, labelId, label, children, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const isRow = useIsInsideFormSection();\n\n if (isRow) {\n return <FormRow label={label} htmlFor={inputId} labelId={labelId}>{children}</FormRow>;\n }\n\n return (\n <FieldFrameRoot spacing={0.75}>\n <FieldFrameLabel component=\"label\" id={labelId} htmlFor={inputId} variant=\"overline\" color=\"text.secondary\">{label}</FieldFrameLabel>\n {children}\n </FieldFrameRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FieldFrame = withPieceSlots(FieldFrameContent, THEME_NAME);\nexport default FieldFrame;\n"],"names":[],"mappings":";;;;;;;;AAoBA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,QAAQ,KAAK;AAC1D,MAAM,kBAAkB,UAAU,YAAY,SAAS,UAAU;AAUjE,SAAS,kBAAkB,OAAO;AAC9B,QAAM,EAAC,SAAS,SAAS,OAAO,UAAU,GAAG,iBAAgB,cAAc,OAAO,UAAU;AAC5F,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,uBAAA;AAEd,MAAI,OAAO;AACP,+BAAQ,SAAA,EAAQ,OAAc,SAAS,SAAS,SAAmB,UAAS;AAAA,EAChF;AAEA,SACI,qBAAC,gBAAA,EAAe,SAAS,MACrB,UAAA;AAAA,IAAA,oBAAC,iBAAA,EAAgB,WAAU,SAAQ,IAAI,SAAS,SAAS,SAAS,SAAQ,YAAW,OAAM,kBAAkB,UAAA,OAAM;AAAA,IAClH;AAAA,EAAA,GACL;AAER;AAGA,MAAM,aAAa,eAAe,mBAAmB,UAAU;"}
1
+ {"version":3,"file":"FieldFrame.js","sources":["../../src/inputs/FieldFrame.jsx"],"sourcesContent":["// El marco de todos los campos del paquete. Fuera de una FormSection, la etiqueta va ARRIBA del campo, ligada a él por su\n// id; dentro de una FormSection (el formulario agrupado de Apple), el campo se vuelve un renglón: la etiqueta a la\n// izquierda y el valor a la derecha (FormRow).\n//\n// La etiqueta va afuera y no flotando adentro del campo: se lee igual con el campo vacío o lleno, y no brinca al escribir.\n//\n// Un campo obligatorio (required) lleva un asterisco en la etiqueta, como en MUI (requiredMark.js: va en el estilo y no como\n// texto, para que la etiqueta siga diciendo lo mismo). Antes no se veía por ningún lado: la etiqueta la pinta este marco y no\n// la de MUI, que es la que ponía el asterisco.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFieldFrame con styleOverrides de sus\n// partes root (la columna) y label (la etiqueta de arriba; el asterisco es su '&[data-required]::after'), y slots/slotProps\n// para cambiarlas. Dentro de una FormSection el\n// renglón es CalcitaFormRow.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {FormRow} from '@/inputs/FormSection.jsx';\nimport {useIsInsideFormSection} from '@/inputs/formLayoutContext.js';\nimport {REQUIRED_LABEL_ATTRIBUTE, requiredMarkSxOf} from '@/inputs/requiredMark.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFieldFrame)\nconst THEME_NAME = 'CalcitaFieldFrame';\nconst FieldFrameRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FieldFrameLabel = piecePart(THEME_NAME, 'Label', Typography);\n\n/**\n * @param {object} props\n * @param {string} props.inputId el id del campo (la etiqueta lo nombra para el lector de pantalla)\n * @param {string} [props.labelId] el id de la etiqueta, para un campo que no es un <input> (la lista desplegable de MUI es un\n * div: `for` no la une con su etiqueta y el campo la nombra con aria-labelledby)\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {boolean} [props.required] el asterisco en la etiqueta\n * @param {import('react').ReactNode} props.children el campo\n */\nfunction FieldFrameContent(props) {\n const {inputId, labelId, label, required = false, children, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const isRow = useIsInsideFormSection();\n const {t} = useToolkitTranslation('common');\n\n if (isRow) {\n return <FormRow label={label} htmlFor={inputId} labelId={labelId} required={required}>{children}</FormRow>;\n }\n\n return (\n <FieldFrameRoot spacing={0.75}>\n <FieldFrameLabel component=\"label\" id={labelId} htmlFor={inputId} variant=\"overline\" color=\"text.secondary\"\n {...(required && {[REQUIRED_LABEL_ATTRIBUTE]: 'true'})} ownSx={required ? requiredMarkSxOf(t('form.requiredMark')) : undefined}>\n {label}\n </FieldFrameLabel>\n {children}\n </FieldFrameRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FieldFrame = withPieceSlots(FieldFrameContent, THEME_NAME);\nexport default FieldFrame;\n"],"names":[],"mappings":";;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,QAAQ,KAAK;AAC1D,MAAM,kBAAkB,UAAU,YAAY,SAAS,UAAU;AAWjE,SAAS,kBAAkB,OAAO;AAC9B,QAAM,EAAC,SAAS,SAAS,OAAO,WAAW,OAAO,UAAU,GAAG,aAAA,IAAgB,cAAc,OAAO,UAAU;AAC9G,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,uBAAA;AACd,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAE1C,MAAI,OAAO;AACP,+BAAQ,SAAA,EAAQ,OAAc,SAAS,SAAS,SAAkB,UAAqB,UAAS;AAAA,EACpG;AAEA,SACI,qBAAC,gBAAA,EAAe,SAAS,MACrB,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAgB,WAAU;AAAA,QAAQ,IAAI;AAAA,QAAS,SAAS;AAAA,QAAS,SAAQ;AAAA,QAAW,OAAM;AAAA,QACzE,GAAI,YAAY,EAAC,CAAC,wBAAwB,GAAG,OAAA;AAAA,QAAU,OAAO,WAAW,iBAAiB,EAAE,mBAAmB,CAAC,IAAI;AAAA,QACjI,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEJ;AAAA,EAAA,GACL;AAER;AAGA,MAAM,aAAa,eAAe,mBAAmB,UAAU;"}