@stackcraftmx/calcita 0.52.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/README.md +53 -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/forms/CalcitaFormik.js +11 -0
  23. package/dist/forms/CalcitaFormik.js.map +1 -0
  24. package/dist/forms/FormAttachmentsField.js +1 -1
  25. package/dist/forms/FormAttachmentsField.js.map +1 -1
  26. package/dist/forms/FormCatalogField.js +1 -1
  27. package/dist/forms/FormCatalogField.js.map +1 -1
  28. package/dist/forms/FormCheckboxGroupField.js +1 -1
  29. package/dist/forms/FormCheckboxGroupField.js.map +1 -1
  30. package/dist/forms/FormColorField.js +1 -1
  31. package/dist/forms/FormColorField.js.map +1 -1
  32. package/dist/forms/FormFileField.js +1 -1
  33. package/dist/forms/FormFileField.js.map +1 -1
  34. package/dist/forms/FormFileUploadField.js +1 -1
  35. package/dist/forms/FormFileUploadField.js.map +1 -1
  36. package/dist/forms/FormImageField.js +1 -1
  37. package/dist/forms/FormImageField.js.map +1 -1
  38. package/dist/forms/FormMaskedField.js +1 -1
  39. package/dist/forms/FormMaskedField.js.map +1 -1
  40. package/dist/forms/FormNumberField.js +1 -1
  41. package/dist/forms/FormNumberField.js.map +1 -1
  42. package/dist/forms/FormSchemaProvider.js +17 -0
  43. package/dist/forms/FormSchemaProvider.js.map +1 -0
  44. package/dist/forms/FormSelectField.js +1 -1
  45. package/dist/forms/FormSelectField.js.map +1 -1
  46. package/dist/forms/FormTextField.js +1 -1
  47. package/dist/forms/FormTextField.js.map +1 -1
  48. package/dist/forms/FormTransferListField.js +1 -1
  49. package/dist/forms/FormTransferListField.js.map +1 -1
  50. package/dist/forms/FormWizard.js +2 -0
  51. package/dist/forms/FormWizard.js.map +1 -1
  52. package/dist/forms/schemaRequired.js +33 -0
  53. package/dist/forms/schemaRequired.js.map +1 -0
  54. package/dist/forms/useFormikField.js +18 -5
  55. package/dist/forms/useFormikField.js.map +1 -1
  56. package/dist/forms.js +52 -46
  57. package/dist/forms.js.map +1 -1
  58. package/dist/hookForm/FormAttachmentsField.js +1 -1
  59. package/dist/hookForm/FormAttachmentsField.js.map +1 -1
  60. package/dist/hookForm/FormCatalogField.js +1 -1
  61. package/dist/hookForm/FormCatalogField.js.map +1 -1
  62. package/dist/hookForm/FormCheckboxGroupField.js +1 -1
  63. package/dist/hookForm/FormCheckboxGroupField.js.map +1 -1
  64. package/dist/hookForm/FormColorField.js +1 -1
  65. package/dist/hookForm/FormColorField.js.map +1 -1
  66. package/dist/hookForm/FormFileField.js +1 -1
  67. package/dist/hookForm/FormFileField.js.map +1 -1
  68. package/dist/hookForm/FormFileUploadField.js +1 -1
  69. package/dist/hookForm/FormFileUploadField.js.map +1 -1
  70. package/dist/hookForm/FormImageField.js +1 -1
  71. package/dist/hookForm/FormImageField.js.map +1 -1
  72. package/dist/hookForm/FormMaskedField.js +1 -1
  73. package/dist/hookForm/FormMaskedField.js.map +1 -1
  74. package/dist/hookForm/FormNumberField.js +1 -1
  75. package/dist/hookForm/FormNumberField.js.map +1 -1
  76. package/dist/hookForm/FormSelectField.js +1 -1
  77. package/dist/hookForm/FormSelectField.js.map +1 -1
  78. package/dist/hookForm/FormTextField.js +1 -1
  79. package/dist/hookForm/FormTextField.js.map +1 -1
  80. package/dist/hookForm/FormTransferListField.js +1 -1
  81. package/dist/hookForm/FormTransferListField.js.map +1 -1
  82. package/dist/hookForm/FormWizard.js +2 -0
  83. package/dist/hookForm/FormWizard.js.map +1 -1
  84. package/dist/hookForm/useHookFormField.js +9 -3
  85. package/dist/hookForm/useHookFormField.js.map +1 -1
  86. package/dist/index.js +4 -2
  87. package/dist/inputs/CatalogInput.js +3 -2
  88. package/dist/inputs/CatalogInput.js.map +1 -1
  89. package/dist/inputs/CheckboxGroupInput.js +2 -1
  90. package/dist/inputs/CheckboxGroupInput.js.map +1 -1
  91. package/dist/inputs/ColorInput.js +3 -1
  92. package/dist/inputs/ColorInput.js.map +1 -1
  93. package/dist/inputs/FieldFrame.js +18 -3
  94. package/dist/inputs/FieldFrame.js.map +1 -1
  95. package/dist/inputs/FormSection.js +42 -11
  96. package/dist/inputs/FormSection.js.map +1 -1
  97. package/dist/inputs/NumberInput.js +3 -1
  98. package/dist/inputs/NumberInput.js.map +1 -1
  99. package/dist/inputs/SelectInput.js +3 -1
  100. package/dist/inputs/SelectInput.js.map +1 -1
  101. package/dist/inputs/TextInput.js +1 -1
  102. package/dist/inputs/TextInput.js.map +1 -1
  103. package/dist/inputs/TransferListInput.js +2 -1
  104. package/dist/inputs/TransferListInput.js.map +1 -1
  105. package/dist/inputs/WizardLayout.js +12 -6
  106. package/dist/inputs/WizardLayout.js.map +1 -1
  107. package/dist/inputs/formLayoutContext.js +7 -2
  108. package/dist/inputs/formLayoutContext.js.map +1 -1
  109. package/dist/inputs/requiredMark.js +17 -0
  110. package/dist/inputs/requiredMark.js.map +1 -0
  111. package/dist/locales/en.js +1 -0
  112. package/dist/locales/en.js.map +1 -1
  113. package/dist/locales/es.js +1 -0
  114. package/dist/locales/es.js.map +1 -1
  115. package/dist/richText/FormRichTextField.js +1 -1
  116. package/dist/richText/FormRichTextField.js.map +1 -1
  117. package/dist/richText/RichTextEditor.js +4 -3
  118. package/dist/richText/RichTextEditor.js.map +1 -1
  119. package/dist/toolkit/ToolkitProvider.js +3 -0
  120. package/dist/toolkit/ToolkitProvider.js.map +1 -1
  121. package/dist/toolkit/toolkitContext.js.map +1 -1
  122. package/dist/types/datePickers/DateInput.d.ts +2 -0
  123. package/dist/types/datePickers/DateTimeInput.d.ts +2 -0
  124. package/dist/types/datePickers/FormDateField.d.ts +3 -3
  125. package/dist/types/datePickers/FormDateTimeField.d.ts +3 -3
  126. package/dist/types/datePickers/FormMonthField.d.ts +3 -3
  127. package/dist/types/datePickers/FormTimeField.d.ts +3 -3
  128. package/dist/types/datePickers/MonthInput.d.ts +2 -0
  129. package/dist/types/datePickers/PickerFrame.d.ts +3 -1
  130. package/dist/types/datePickers/TimeInput.d.ts +2 -0
  131. package/dist/types/feedback/Toaster.d.ts +15 -1
  132. package/dist/types/feedback/index.d.ts +1 -1
  133. package/dist/types/forms/CalcitaFormik.d.ts +6 -0
  134. package/dist/types/forms/FormAttachmentsField.d.ts +3 -3
  135. package/dist/types/forms/FormCatalogField.d.ts +2 -2
  136. package/dist/types/forms/FormCheckboxGroupField.d.ts +3 -3
  137. package/dist/types/forms/FormColorField.d.ts +2 -2
  138. package/dist/types/forms/FormFileField.d.ts +3 -3
  139. package/dist/types/forms/FormFileUploadField.d.ts +3 -3
  140. package/dist/types/forms/FormImageField.d.ts +3 -3
  141. package/dist/types/forms/FormMaskedField.d.ts +2 -2
  142. package/dist/types/forms/FormNumberField.d.ts +2 -2
  143. package/dist/types/forms/FormSchemaProvider.d.ts +13 -0
  144. package/dist/types/forms/FormSelectField.d.ts +2 -2
  145. package/dist/types/forms/FormTextField.d.ts +2 -2
  146. package/dist/types/forms/FormTransferListField.d.ts +3 -3
  147. package/dist/types/forms/FormWizard.d.ts +3 -1
  148. package/dist/types/forms/index.d.ts +1 -0
  149. package/dist/types/forms/schemaRequired.d.ts +14 -0
  150. package/dist/types/forms/useFormikField.d.ts +10 -4
  151. package/dist/types/forms.d.ts +4 -2
  152. package/dist/types/hookForm/FormAttachmentsField.d.ts +3 -3
  153. package/dist/types/hookForm/FormCatalogField.d.ts +3 -3
  154. package/dist/types/hookForm/FormCheckboxGroupField.d.ts +3 -3
  155. package/dist/types/hookForm/FormColorField.d.ts +3 -3
  156. package/dist/types/hookForm/FormFileField.d.ts +3 -3
  157. package/dist/types/hookForm/FormFileUploadField.d.ts +3 -3
  158. package/dist/types/hookForm/FormImageField.d.ts +3 -3
  159. package/dist/types/hookForm/FormMaskedField.d.ts +3 -3
  160. package/dist/types/hookForm/FormNumberField.d.ts +3 -3
  161. package/dist/types/hookForm/FormSelectField.d.ts +3 -3
  162. package/dist/types/hookForm/FormTextField.d.ts +3 -3
  163. package/dist/types/hookForm/FormTransferListField.d.ts +3 -3
  164. package/dist/types/hookForm/FormWizard.d.ts +3 -1
  165. package/dist/types/hookForm/useHookFormField.d.ts +8 -4
  166. package/dist/types/index.d.ts +2 -2
  167. package/dist/types/inputs/CatalogInput.d.ts +1 -0
  168. package/dist/types/inputs/CheckboxGroupInput.d.ts +1 -0
  169. package/dist/types/inputs/ColorInput.d.ts +1 -0
  170. package/dist/types/inputs/FieldFrame.d.ts +1 -0
  171. package/dist/types/inputs/FormSection.d.ts +13 -0
  172. package/dist/types/inputs/NumberInput.d.ts +1 -0
  173. package/dist/types/inputs/SelectInput.d.ts +1 -0
  174. package/dist/types/inputs/TextInput.d.ts +1 -1
  175. package/dist/types/inputs/TransferListInput.d.ts +1 -0
  176. package/dist/types/inputs/WizardLayout.d.ts +1 -0
  177. package/dist/types/inputs/formLayoutContext.d.ts +8 -1
  178. package/dist/types/inputs/requiredMark.d.ts +9 -0
  179. package/dist/types/locales/en.d.ts +1 -0
  180. package/dist/types/locales/es.d.ts +1 -0
  181. package/dist/types/richText/FormRichTextField.d.ts +3 -3
  182. package/dist/types/richText/RichTextEditor.d.ts +1 -0
  183. package/dist/types/toolkit/ToolkitProvider.d.ts +5 -1
  184. package/dist/types/toolkit/toolkitContext.d.ts +2 -1
  185. package/dist/types/upload/AttachmentsInput.d.ts +2 -0
  186. package/dist/types/upload/FileInput.d.ts +1 -0
  187. package/dist/types/upload/FileUploadInput.d.ts +1 -0
  188. package/dist/types/upload/ImageInput.d.ts +1 -0
  189. package/dist/upload/AttachmentsInput.js +2 -1
  190. package/dist/upload/AttachmentsInput.js.map +1 -1
  191. package/dist/upload/FileInput.js +2 -1
  192. package/dist/upload/FileInput.js.map +1 -1
  193. package/dist/upload/FileUploadInput.js +2 -1
  194. package/dist/upload/FileUploadInput.js.map +1 -1
  195. package/dist/upload/ImageInput.js +2 -1
  196. package/dist/upload/ImageInput.js.map +1 -1
  197. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import ColorInput from "../inputs/ColorInput.js";
4
4
  import { useHookFormField } from "./useHookFormField.js";
5
5
  function FormColorField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(ColorInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormColorField.js","sources":["../../src/hookForm/FormColorField.jsx"],"sourcesContent":["// Campo de color de react-hook-form: ColorInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormColorField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport ColorInput from '@/inputs/ColorInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/ColorInput.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 FormColorField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <ColorInput {...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":"FormColorField.js","sources":["../../src/hookForm/FormColorField.jsx"],"sourcesContent":["// Campo de color de react-hook-form: ColorInput (inputs/) conectado a su campo con useController. Se ve y se porta igual que\n// el FormColorField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport ColorInput from '@/inputs/ColorInput.jsx';\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/inputs/ColorInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/inputs/ColorInput.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 FormColorField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <ColorInput {...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 FileInput from "../upload/FileInput.js";
5
5
  function FormFileField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(FileInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormFileField.js","sources":["../../src/hookForm/FormFileField.jsx"],"sourcesContent":["// Campo de un archivo de react-hook-form: FileInput (upload/) conectado a su campo con useController. Se ve y se porta igual\n// que el FormFileField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport FileInput from '@/upload/FileInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/upload/FileInput.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 FormFileField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <FileInput {...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":"FormFileField.js","sources":["../../src/hookForm/FormFileField.jsx"],"sourcesContent":["// Campo de un archivo de react-hook-form: FileInput (upload/) conectado a su campo con useController. Se ve y se porta igual\n// que el FormFileField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport FileInput from '@/upload/FileInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/upload/FileInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/upload/FileInput.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 FormFileField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <FileInput {...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 FileUploadInput from "../upload/FileUploadInput.js";
5
5
  function FormFileUploadField({ name, control, ...inputProps }) {
6
- const fieldProps = useHookFormField(name, control);
6
+ const fieldProps = useHookFormField(name, control, inputProps);
7
7
  return /* @__PURE__ */ jsx(FileUploadInput, { ...inputProps, ...fieldProps });
8
8
  }
9
9
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"FormFileUploadField.js","sources":["../../src/hookForm/FormFileUploadField.jsx"],"sourcesContent":["// El cargador mini de react-hook-form: FileUploadInput (upload/) conectado a su campo con useController. Se ve y se porta\n// igual que el FormFileUploadField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport FileUploadInput from '@/upload/FileUploadInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/upload/FileUploadInput.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 FormFileUploadField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control);\n return <FileUploadInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,oBAAoB,EAAC,MAAM,SAAS,GAAG,cAAa;AACxE,QAAM,aAAa,iBAAiB,MAAM,OAAO;AACjD,SAAO,oBAAC,iBAAA,EAAiB,GAAG,YAAa,GAAG,WAAA,CAAY;AAC5D;"}
1
+ {"version":3,"file":"FormFileUploadField.js","sources":["../../src/hookForm/FormFileUploadField.jsx"],"sourcesContent":["// El cargador mini de react-hook-form: FileUploadInput (upload/) conectado a su campo con useController. Se ve y se porta\n// igual que el FormFileUploadField de formik (@stackcraftmx/calcita/forms).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useHookFormField} from '@/hookForm/useHookFormField.js';\nimport FileUploadInput from '@/upload/FileUploadInput.jsx';\n\n/**\n * @param {{name: string, control?: object} & Omit<Parameters<typeof import('@/upload/FileUploadInput.jsx').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('@/upload/FileUploadInput.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 FormFileUploadField({name, control, ...inputProps}) {\n const fieldProps = useHookFormField(name, control, inputProps);\n return <FileUploadInput {...inputProps} {...fieldProps} />;\n}\n"],"names":[],"mappings":";;;;AAWA,SAAwB,oBAAoB,EAAC,MAAM,SAAS,GAAG,cAAa;AACxE,QAAM,aAAa,iBAAiB,MAAM,SAAS,UAAU;AAC7D,SAAO,oBAAC,iBAAA,EAAiB,GAAG,YAAa,GAAG,WAAA,CAAY;AAC5D;"}
@@ -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";
@@ -120,7 +120,7 @@ import { default as default69 } from "./inputs/ColorInput.js";
120
120
  import { default as default70 } from "./inputs/FieldFrame.js";
121
121
  import { default as default71 } from "./inputs/MaskedInput.js";
122
122
  import { INPUT_MASK, isMaskComplete, maskPatternOf, maskedTextOf, rawValueOf } from "./inputs/inputMasks.js";
123
- import { FORM_ROW_CLASS, FormRow, default as default72 } from "./inputs/FormSection.js";
123
+ import { FORM_LABEL_PLACEMENT, FORM_ROW_CLASS, FormRow, default as default72 } from "./inputs/FormSection.js";
124
124
  import { default as default73 } from "./inputs/CheckboxGroupInput.js";
125
125
  import { default as default74 } from "./inputs/NumberInput.js";
126
126
  import { default as default75 } from "./inputs/TransferListInput.js";
@@ -236,6 +236,7 @@ export {
236
236
  FILTER_DRAWER_ANCHOR,
237
237
  FILTER_FIELD_KIND,
238
238
  FONT_SIZE_LEVELS,
239
+ FORM_LABEL_PLACEMENT,
239
240
  FORM_ROW_CLASS,
240
241
  default70 as FieldFrame,
241
242
  default25 as FileDropzone,
@@ -319,6 +320,7 @@ export {
319
320
  default8 as StaleBuildBoundary,
320
321
  default59 as StatusChip,
321
322
  THEME_LOOK,
323
+ TOASTER_POSITION,
322
324
  TOAST_DEFAULT_OPTIONS,
323
325
  TOP_BAR,
324
326
  TREND_DIRECTION,
@@ -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":";;;;;;;;;;;;AA6BA,MAAM,aAAa;AACnB,MAAM,oBAAoB,UAAU,YAAY,SAAS,YAAY;AAErE,MAAM,sBAAsB;AAE5B,MAAM,kBAAkB;AAQxB,SAAS,YAAY,MAAM;AACvB,SAAO,OAAO,QAAQ,EAAE,EAAE,UAAU,KAAK,EAAE,QAAQ,WAAA,kBAAA,IAAA,GAAmB,EAAE,EAAE,YAAA;AAC9E;AA2BA,SAAS,oBAAoB,OAAO;AAChC,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ,UAAU,CAAA;AAAA,IAAI;AAAA,IAAa;AAAA,IAAO,aAAa;AAAA,IAAK,UAAU;AAAA,IAAO,WAAW;AAAA,IAAO,WAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IACzJ,WAAW;AAAA,IAAO,YAAY;AAAA,IAAO,aAAa;AAAA,IAAO;AAAA,IAAW;AAAA,IAAU;AAAA,IAC9E,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,QAAM,mBAAmB,kBAAkB,EAAC,MAAM,OAAO,UAAS;AAClE,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,SAAS,IAAI,WAAW;AACnD,QAAM,YAAY,iBAAiB,KAAK;AAExC,QAAM,iBAAiB,YAChB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAA,GAAI,IAAI,CAAC,SAAS,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,IAAI,CAAC,EAAE,OAAO,OAAO,IAChH,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,KAAK,KAAK;AACzD,QAAM,gBAAgB,CAAC,WAAY,cAAc,YAAY,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,OAAO,IAAI;AAEtG,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,EAAE;AAC/C,QAAM,gBAAgB,CAAC,QAAQ,SAAS;AACpC,UAAM,aAAa,YAAY,IAAI;AACnC,WAAO,CAAC,cAAc,YAAY,cAAc,MAAM,CAAC,EAAE,SAAS,UAAU;AAAA,EAChF;AACA,QAAM,gBAAgB,YAAY,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAA;AACjE,QAAM,gBAAgB,QAAQ,OAAO,CAAC,WAAW,cAAc,QAAQ,UAAU,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,IAAI;AAC/G,QAAM,iBAAiB,iBAAiB,eAAe,aAAa;AACpE,QAAM,kBAAkB,MAAM;AAC1B,QAAI,WAAW,QAAQ;AACnB,aAAO,eAAe,YAAY,EAAE,yBAAyB,EAAC,OAAO,cAAc,OAAA,CAAO,IAAI,EAAE,uBAAuB,EAAC,OAAO,cAAc,QAAO;AAAA,IACxJ;AACA,WAAO,eAAe,YAAY,EAAE,kBAAkB,IAAI,EAAE,kBAAkB,EAAC,OAAO,cAAc,OAAA,CAAO;AAAA,EAC/G,GAAA;AACA,QAAM,iBAAiB,YAAY;AAEnC,SACI,oBAAC,YAAA,EAAW,SAAkB,OAAc,UACxC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,IAAI;AAAA,MACJ;AAAA,MACA,sBAAsB;AAAA,MACtB,WAAS;AAAA,MACT,MAAK;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,eAAe,EAAE,gBAAgB;AAAA,MACjC,aAAa,EAAE,qBAAqB;AAAA,MACpC,gBAAgB,CAAC,WAAY,OAAO,SAAS,kBAAkB,iBAAiB,cAAc,MAAM;AAAA,MACpG,sBAAsB,CAAC,QAAQ,kBAAkB,OAAO,SAAS,cAAc;AAAA,MAC/E,eAAe,CAAC,YAAY,EAAC,iBAAgB;AACzC,cAAM,kBAAkB,WAAW,OAAO,CAAC,WAAW,cAAc,QAAQ,UAAU,CAAC,EAAE,MAAM,GAAG,mBAAmB;AAErH,eAAO,kBAAkB,gBAAgB,SAAS,IAAI,CAAC,EAAC,MAAM,iBAAiB,OAAO,eAAA,GAAiB,GAAG,eAAe,IAAI;AAAA,MACjI;AAAA,MAGA,YAAY,iBAAiB,aAAa;AAAA,MAC1C,eAAe,CAAC,OAAO,MAAM,WAAW;AAGpC,cAAM,oBAAoB,CAAC,gBAAgB,gBAAgB,OAAO,EAAE,SAAS,MAAM,KAAK,OAAO,SAAS;AACxG,YAAI,EAAE,kBAAkB,oBAAoB;AACxC,wBAAc,IAAI;AAAA,QACtB;AAAA,MACJ;AAAA,MACA,UAAU,CAAC,OAAO,QAAQ,QAAQ,YAAY;AAC1C,YAAI,CAAC,UAAU;AACX,mBAAS,QAAQ,QAAQ,EAAE;AAC3B;AAAA,QACJ;AACA,YAAI,SAAS,QAAQ,SAAS,iBAAiB;AAC3C,mBAAS,eAAe,SAAS;AACjC;AAAA,QACJ;AACA,iBAAS,OAAO,OAAO,CAAC,iBAAiB,aAAa,SAAS,eAAe,EAAE,IAAI,CAAC,iBAAiB,aAAa,IAAI,CAAC;AAAA,MAC5H;AAAA,MACA,WAAW,WAAW,YAAY;AAAA,MAClC,kBAAkB,CAAC,gBAAgB,EAAE,qBAAqB,EAAC,OAAO,aAAY;AAAA,MAC9E,cAAc,aAAa,cAAc,aAAa,CAAC,aAAa,QAAQ,EAAC,eAAc;AAEvF,cAAM,EAAC,KAAK,GAAG,QAAA,IAAW;AAC1B,cAAM,cAAc,OAAO,SAAS;AACpC,cAAM,WAAW,cAAc;AAC/B,eACI;AAAA,UAAC;AAAA,UAAA;AAAA,YAAc,GAAG;AAAA,YAAU,GAAI,eAAe,EAAC,iBAAiB,eAAe,UAAA;AAAA,YAC5E,OAAO,cAAc,EAAC,YAAY,QAAO;AAAA,YACxC,UAAA;AAAA,cAAA,YACG;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBAAS,MAAK;AAAA,kBAAQ,UAAU;AAAA,kBAAI,eAAa;AAAA,kBAAC,IAAI,EAAC,GAAG,GAAG,IAAI,EAAA;AAAA,kBACxD,SAAS,cAAc,eAAe,YAAY;AAAA,kBAClD,eAAe,eAAe,eAAe;AAAA,gBAAA;AAAA,cAAA;AAAA,cAE1D,cAAc,iBAAiB,cAAc,MAAM;AAAA,YAAA;AAAA,UAAA;AAAA,UAP/C;AAAA,QAAA;AAAA,MAUjB,IAAI;AAAA,MACJ;AAAA,MACA,aAAa,CAAC,eACV,oBAAC,WAAA,EAAW,GAAG,YAAY,YAAY,EAAC,GAAG,WAAW,YAAY,iBAAiB,YAAY,UAAY,MAAY,UAAoB,aAA0B,OAAO,QAAQ,SAAS,GAAG,YAAY,aAAa,WAAA,CAAY;AAAA,IAAA;AAAA,EAAA,GAGjP;AAER;AAGA,MAAM,eAAe,eAAe,qBAAqB,UAAU;"}
@@ -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":";;;;;;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,oBAAoB,UAAU,YAAY,QAAQ,SAAS;AACjE,MAAM,yBAAyB,UAAU,YAAY,aAAa,gBAAgB;AAClF,MAAM,sBAAsB,UAAU,YAAY,UAAU,gBAAgB;AAoB5E,SAAS,0BAA0B,OAAO;AACtC,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAS,MAAM;AAAA,IAAO,YAAY;AAAA,IAAO;AAAA,IAAO,aAAa;AAAA,IAAK,WAAW;AAAA,IAAO;AAAA,IAAI;AAAA,IAAU;AAAA,IACxI,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,QAAM,mBAAmB,kBAAkB,EAAC,MAAM,OAAO,UAAS;AAClE,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,gBAAgB,IAAI,WAAW;AAC1D,QAAM,UAAU,GAAG,OAAO;AAC1B,QAAM,YAAY,iBAAiB,KAAK;AACxC,QAAM,gBAAgB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAA;AACrD,QAAM,iBAAiB,iBAAiB,eAAe,QAAQ,OAAO,CAAC,WAAW,CAAC,OAAO,QAAQ,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC;AAEjI,QAAM,iBAAiB,CAAC,WAAW,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,EAAE,OAAO,CAAC,gBAAgB,OAAO,SAAS,WAAW,CAAC;AAC7H,QAAM,SAAS,CAAC,WAAW;AACvB,aAAS,eAAe,MAAM,CAAC;AAC/B,aAAA;AAAA,EACJ;AACA,QAAM,SAAS,CAAC,gBAAgB,OAAO,cAAc,SAAS,WAAW,IACnE,cAAc,OAAO,CAAC,iBAAiB,iBAAiB,WAAW,IACnE,CAAC,GAAG,eAAe,WAAW,CAAC;AAErC,SACI,oBAAC,cAAW,SAAS,GAAG,OAAO,UAAU,SAAkB,OACvD,UAAA,qBAAC,OAAA,EACG,UAAA;AAAA,IAAA,qBAAC,mBAAA,EAAkB,KAAU,MAAK,SAAQ,mBAAiB,SAAS,OAAO,EAAC,WAAW,EAAA,GAAI,IACtF,UAAA;AAAA,MAAA,aACG;AAAA,QAAC;AAAA,QAAA;AAAA,UACG;AAAA,UACA,OAAO,eAAe,YAAY,EAAE,kBAAkB,IAAI,EAAE,kBAAkB,EAAC,OAAO,QAAQ,OAAA,CAAO;AAAA,UACrG,SACI;AAAA,YAAC;AAAA,YAAA;AAAA,cAAS,MAAK;AAAA,cAAQ,SAAS,eAAe;AAAA,cAAW,eAAe,eAAe;AAAA,cAC9E,UAAU,MAAM,OAAO,eAAe,SAAS;AAAA,YAAA;AAAA,UAAA;AAAA,UAE7D,OAAO,EAAC,gCAAgC,EAAC,YAAY,MAAG;AAAA,QAAC;AAAA,MAAA;AAAA,MAGhE,QAAQ,IAAI,CAAC,QAAQ,UAClB;AAAA,QAAC;AAAA,QAAA;AAAA,UAEG,UAAU,YAAY,OAAO;AAAA,UAC7B,OAAO,EAAC,YAAY,OAAO,cAAc,eAAe,SAAA;AAAA,UACxD,SACI;AAAA,YAAC;AAAA,YAAA;AAAA,cAAS,MAAK;AAAA,cAAQ;AAAA,cAAY,IAAI,UAAU,IAAI,GAAG,OAAO,WAAW;AAAA,cAChE,SAAS,cAAc,SAAS,OAAO,KAAK;AAAA,cAAG,UAAU,MAAM,OAAO,OAAO,KAAK;AAAA,cAClF,IAAI,OAAO,cAAc,EAAC,IAAI,SAAQ;AAAA,YAAA;AAAA,UAAA;AAAA,UAEpD,OAAO,OAAO,cACV,qBAAC,OAAA,EAAM,WAAU,QACb,UAAA;AAAA,YAAA,oBAAC,cAAW,WAAU,QAAO,SAAQ,SAAS,iBAAO,OAAM;AAAA,YAC3D,oBAAC,cAAW,WAAU,QAAO,SAAQ,WAAU,OAAM,kBAAkB,UAAA,OAAO,YAAA,CAAY;AAAA,UAAA,EAAA,CAC9F,IACA,OAAO;AAAA,QAAA;AAAA,QAbN,OAAO;AAAA,MAAA,CAenB;AAAA,IAAA,GACL;AAAA,wBACC,gBAAA,EAAe,OAAO,QAAQ,SAAS,GAAI,uBAAa,WAAA,CAAW;AAAA,EAAA,EAAA,CACxE,EAAA,CACJ;AAER;AAGA,MAAM,qBAAqB,eAAe,2BAA2B,UAAU;"}
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":";;;;;;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,cAAc,UAAU,YAAY,UAAU,GAAG;AACvD,MAAM,mBAAmB,UAAU,YAAY,eAAe,GAAG;AAGjE,MAAM,eAAe;AAErB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AAEvB,MAAM,gBAAgB;AACtB,MAAM,cAAc,OAAO,OAAO,EAAC,OAAO,0BAA0B,MAAM,6BAA4B;AAkBtG,SAAS,kBAAkB,OAAO;AAE9B,QAAM;AAAA,IACN;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO,aAAa;AAAA,IAAK,gBAAgB;AAAA,IAAM,WAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAAU;AAAA,IAC3H,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AAC/B,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,QAAM,mBAAmB,kBAAkB,EAAC,MAAM,OAAO,UAAS;AAClE,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,OAAO,IAAI,WAAW;AACjD,QAAM,QAAQ,SAAS;AACvB,QAAM,eAAe,UAAU,KAAK,KAAK;AACzC,QAAM,gBAAgB,eAAe,gBAAgB,KAAK,IAAI;AAC9D,QAAM,aAAa,iBAAiB,gBAAgB,gBAAgB,eAAe,kBAAkB,KAAK,IAAI;AAC9G,QAAM,YAAY,iBAAiB,KAAK;AACxC,QAAM,eAAe,iBAAiB,gBAAgB,EAAE,sBAAsB,EAAC,OAAO,cAAc,QAAQ,cAAc,EAAA,CAAE,IAAI;AAChI,QAAM,SAAS,CAAC,cAAc,SAAS,UAAU,aAAa;AAE9D,6BACK,YAAA,EAAW,SAAkB,OAC1B,UAAA,qBAAC,OAAA,EAAM,SAAS,GAIZ,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACG;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,QACA,MAAK;AAAA,QACL,WAAS;AAAA,QACT,OAAO;AAAA,QACP;AAAA,QACA,aAAY;AAAA,QACZ,UAAU,CAAC,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,QAC9C;AAAA,QACA,OAAO,QAAQ,SAAS;AAAA,QACxB,YAAY,aAAa;AAAA,QACzB,WAAW;AAAA,UACP,OAAO;AAAA,YACH,gBACI,oBAAC,gBAAA,EAAe,UAAS,SACrB,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBAAY,WAAU;AAAA,gBAClB,OAAO,CAAC,WAAW;AAAA,kBACf,UAAU;AAAA,kBAAY,SAAS;AAAA,kBAAS,YAAY;AAAA,kBAAG,OAAO;AAAA,kBAAe,QAAQ;AAAA,kBACrF,cAAc;AAAA,kBAAO,QAAQ,WAAW,YAAY;AAAA,kBACpD,iBAAiB,eAAe,QAAQ;AAAA;AAAA,kBAExC,WAAW,mBAAmB,YAAY,KAAK;AAAA,kBAC/C,GAAG,MAAM,YAAY,QAAQ,EAAC,WAAW,mBAAmB,YAAY,IAAI,GAAA,CAAG;AAAA,gBAAA;AAAA,gBAEpF,UAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAAiB,WAAU;AAAA,oBAAQ,MAAK;AAAA,oBAAQ,OAAO,eAAe,QAAQ;AAAA,oBAAe;AAAA,oBACzF,cAAY,EAAE,gBAAgB;AAAA,oBAAG,UAAU,CAAC,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,oBAC/E,OAAO,EAAC,UAAU,YAAY,OAAO,GAAG,OAAO,QAAQ,QAAQ,QAAQ,SAAS,GAAG,QAAQ,GAAG,GAAG,GAAG,QAAQ,UAAA;AAAA,kBAAS;AAAA,gBAAA;AAAA,cAAG;AAAA,YAAA,EACjI,CACJ;AAAA,UAAA;AAAA,UAGR,WAAW,EAAC,WAAW,cAAc,YAAY,OAAO,OAAO,EAAC,oBAAoB,eAAA,EAAc;AAAA,QAAC;AAAA,MACvG;AAAA,IAAA;AAAA,IAEH,cACG;AAAA,MAAC;AAAA,MAAA;AAAA,QAAM,UAAS;AAAA,QACT,QAAQ,oBAAC,QAAA,EAAO,OAAM,WAAU,MAAK,SAAQ,SAAS,MAAM,OAAO,UAAU,GAAI,UAAA,EAAE,2BAA2B,EAAC,OAAO,WAAA,CAAW,GAAE;AAAA,QACrI,UAAA,EAAE,yBAAyB,EAAC,WAAA,CAAW;AAAA,MAAA;AAAA,IAAA;AAAA,EAC5C,EAAA,CAER,EAAA,CACJ;AAER;AAGA,MAAM,aAAa,eAAe,mBAAmB,UAAU;"}
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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"}