@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
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @param {{name: string, control?: object} & Omit<Parameters<typeof import('../inputs/TransferListInput.js').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las del campo de la casa, sin
3
- * value / onChange / onBlur / error; control: el de useForm, si la pantalla no usa FormProvider
2
+ * @param {{name: string, control?: object} & Omit<Parameters<typeof import('../inputs/TransferListInput.js').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('../inputs/TransferListInput.js').default>[0], 'onChange'|'onBlur'>>} props las del campo de la casa, sin
3
+ * 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
4
4
  */
5
5
  export default function FormTransferListField({ name, control, ...inputProps }: {
6
6
  name: string;
7
7
  control?: object;
8
- } & Omit<Parameters<typeof import('../inputs/TransferListInput.js').default>[0], 'name' | 'value' | 'onChange' | 'onBlur' | 'error'>): import("react").JSX.Element;
8
+ } & Omit<Parameters<typeof import('../inputs/TransferListInput.js').default>[0], 'name' | 'value' | 'onChange' | 'onBlur' | 'error'> & Partial<Pick<Parameters<typeof import('../inputs/TransferListInput.js').default>[0], 'onChange' | 'onBlur'>>): import("react").JSX.Element;
@@ -9,13 +9,14 @@
9
9
  * @param {boolean} [props.steps[].completed] lleva la palomita (cuando los títulos se pulsan, solo si la pantalla lo dice)
10
10
  * @param {string} props.submitLabel ya traducido: el botón del último paso
11
11
  * @param {'horizontal'|'vertical'} [props.orientation] uno de WIZARD_ORIENTATION; por omisión horizontal
12
+ * @param {boolean} [props.hideBackOnFirstStep] en el primer paso no se pinta «Anterior»; por omisión sale apagado, como antes
12
13
  * @param {string} [props.initialStepKey] la llave del paso en el que se abre (la que diga el servidor); por omisión el primero
13
14
  * @param {'none'|'free'|'checked'} [props.stepSelection] uno de WIZARD_STEP_SELECTION: si el título de otro paso se pulsa y qué
14
15
  * revisa antes; por omisión none (se avanza con «Siguiente», como siempre)
15
16
  * @param {(stepKey: string, index: number) => void} [props.onStepChange] cada vez que cambia el paso abierto
16
17
  * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}
17
18
  */
18
- export default function FormWizard({ steps, submitLabel, orientation, initialStepKey, stepSelection, onStepChange, texts, ...unknownProps }: {
19
+ export default function FormWizard({ steps, submitLabel, orientation, hideBackOnFirstStep, initialStepKey, stepSelection, onStepChange, texts, ...unknownProps }: {
19
20
  steps: {
20
21
  key: string;
21
22
  label: string;
@@ -26,6 +27,7 @@ export default function FormWizard({ steps, submitLabel, orientation, initialSte
26
27
  }[];
27
28
  submitLabel: string;
28
29
  orientation?: 'horizontal' | 'vertical';
30
+ hideBackOnFirstStep?: boolean;
29
31
  initialStepKey?: string;
30
32
  stepSelection?: 'none' | 'free' | 'checked';
31
33
  onStepChange?: (stepKey: string, index: number) => void;
@@ -1,13 +1,17 @@
1
1
  /**
2
2
  * @param {string} name el campo del formulario
3
3
  * @param {object} [control] el de useForm, si la pantalla no usa FormProvider
4
- * @returns {{name: string, value: unknown, onChange: (value: unknown) => void, onBlur: () => void, inputRef: Function, error: unknown}}
4
+ * @param {{onChange?: Function, onBlur?: Function}} [screen] lo que la pantalla le dijo al campo
5
+ * @returns {{name: string, value: unknown, onChange: (value: unknown, ...rest: unknown[]) => void, onBlur: (...args: unknown[]) => void, inputRef: Function, error: unknown}}
5
6
  */
6
- export declare function useHookFormField(name: string, control?: object): {
7
+ export declare function useHookFormField(name: string, control?: object, screen?: {
8
+ onChange?: Function;
9
+ onBlur?: Function;
10
+ }): {
7
11
  name: string;
8
12
  value: unknown;
9
- onChange: (value: unknown) => void;
10
- onBlur: () => void;
13
+ onChange: (value: unknown, ...rest: unknown[]) => void;
14
+ onBlur: (...args: unknown[]) => void;
11
15
  inputRef: Function;
12
16
  error: unknown;
13
17
  };
@@ -10,7 +10,7 @@ export { createPathIndex, fillPath } from './toolkit/pathIndex.js';
10
10
  export { combineTexts } from './toolkit/combineTexts.js';
11
11
  export { default as toolkitTextsEs } from './locales/es.js';
12
12
  export { default as toolkitTextsEn } from './locales/en.js';
13
- export { ConfirmDialog, ModalWaiting, NEW_BUILD_NOTICE_VARIANT, NewBuildNotice, StaleBuildBoundary, TOAST_DEFAULT_OPTIONS, Toaster, confirmActionMachine, dismissToast, errorsObjectToMessage, toastDefault, toastError, toastInfo, toastLoading, toastProblem, toastPromise, toastSuccess, toastWarn, useConfirmAction, } from './feedback/index.js';
13
+ export { ConfirmDialog, ModalWaiting, NEW_BUILD_NOTICE_VARIANT, NewBuildNotice, StaleBuildBoundary, TOAST_DEFAULT_OPTIONS, TOASTER_POSITION, Toaster, confirmActionMachine, dismissToast, errorsObjectToMessage, toastDefault, toastError, toastInfo, toastLoading, toastProblem, toastPromise, toastSuccess, toastWarn, useConfirmAction, } from './feedback/index.js';
14
14
  export { DEFAULT_FILE_TYPES, DOWNLOAD_STATUS, DownloadManager, UNKNOWN_FILE_TYPE, cancelDownload, clearFinishedDownloads, downloadsMachine, startDownload, useFileTypes, } from './downloads/index.js';
15
15
  export { THEME_LOOK, TOP_BAR, createToolkitTheme } from './theme/createToolkitTheme.js';
16
16
  export { isAndroidDevice } from './theme/androidDevice.js';
@@ -68,7 +68,7 @@ export { default as ColorInput } from './inputs/ColorInput.js';
68
68
  export { default as FieldFrame } from './inputs/FieldFrame.js';
69
69
  export { default as MaskedInput } from './inputs/MaskedInput.js';
70
70
  export { INPUT_MASK, isMaskComplete, maskPatternOf, maskedTextOf, rawValueOf } from './inputs/inputMasks.js';
71
- export { FORM_ROW_CLASS, FormRow, default as FormSection } from './inputs/FormSection.js';
71
+ export { FORM_LABEL_PLACEMENT, FORM_ROW_CLASS, FormRow, default as FormSection } from './inputs/FormSection.js';
72
72
  export { default as CheckboxGroupInput } from './inputs/CheckboxGroupInput.js';
73
73
  export { default as NumberInput } from './inputs/NumberInput.js';
74
74
  export { default as TransferListInput } from './inputs/TransferListInput.js';
@@ -20,6 +20,7 @@ declare const CatalogInput: (props: {
20
20
  loading?: boolean;
21
21
  placeholder?: string;
22
22
  disabled?: boolean;
23
+ required?: boolean;
23
24
  inputRef?: import('react').Ref<HTMLInputElement>;
24
25
  multiple?: boolean;
25
26
  selectAll?: boolean;
@@ -18,6 +18,7 @@ declare const CheckboxGroupInput: (props: {
18
18
  };
19
19
  helperText?: string;
20
20
  disabled?: boolean;
21
+ required?: boolean;
21
22
  sx?: object;
22
23
  messages?: Record<string, string | object>;
23
24
  texts?: object;
@@ -11,6 +11,7 @@ declare const ColorInput: (props: {
11
11
  helperText?: string;
12
12
  checkContrast?: boolean;
13
13
  disabled?: boolean;
14
+ required?: boolean;
14
15
  inputRef?: import('react').Ref<HTMLInputElement>;
15
16
  messages?: Record<string, string | object>;
16
17
  texts?: object;
@@ -2,6 +2,7 @@ declare const FieldFrame: (props: {
2
2
  inputId: string;
3
3
  labelId?: string;
4
4
  label: import('react').ReactNode;
5
+ required?: boolean;
5
6
  children: import('react').ReactNode;
6
7
  } & {
7
8
  slots?: Record<string, import('react').ElementType>;
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Dónde va la etiqueta de cada renglón: a la izquierda siempre (side, lo de siempre) o arriba del campo en el teléfono
3
+ * (topOnPhone: por debajo de `sm` en el tema; más ancho, a la izquierda).
4
+ */
5
+ export declare const FORM_LABEL_PLACEMENT: Readonly<{
6
+ side: "side";
7
+ topOnPhone: "topOnPhone";
8
+ }>;
1
9
  /** La clase de cada renglón: la sección le dibuja su línea de arriba, esté donde esté adentro de la tarjeta. */
2
10
  export declare const FORM_ROW_CLASS = "calcita-form-row";
3
11
  /**
@@ -8,22 +16,27 @@ export declare const FORM_ROW_CLASS = "calcita-form-row";
8
16
  * @param {import('react').ReactNode} props.label ya traducida
9
17
  * @param {string} [props.htmlFor] el id del campo que nombra la etiqueta
10
18
  * @param {string} [props.labelId] el id de la etiqueta (para un campo que la nombra con aria-labelledby)
19
+ * @param {boolean} [props.required] el asterisco en la etiqueta (el lector de pantalla no lo lee: el campo ya lo dice)
11
20
  * @param {import('react').ReactNode} props.children
12
21
  */
13
22
  export declare function FormRow(props: {
14
23
  label: import('react').ReactNode;
15
24
  htmlFor?: string;
16
25
  labelId?: string;
26
+ required?: boolean;
17
27
  children: import('react').ReactNode;
18
28
  }): import("react").JSX.Element;
19
29
  /**
20
30
  * @param {object} props
21
31
  * @param {string} [props.title] ya traducido; va arriba de la tarjeta
22
32
  * @param {import('react').ReactNode} [props.footer] la explicación, abajo de la tarjeta
33
+ * @param {string} [props.labelPlacement] uno de FORM_LABEL_PLACEMENT: side (por omisión, la etiqueta a la izquierda) o
34
+ * topOnPhone (arriba del campo por debajo de `sm`)
23
35
  * @param {import('react').ReactNode} props.children los campos, directo o dentro de un Grid, un Stack o un componente propio
24
36
  */
25
37
  export default function FormSection(props: {
26
38
  title?: string;
27
39
  footer?: import('react').ReactNode;
40
+ labelPlacement?: string;
28
41
  children: import('react').ReactNode;
29
42
  }): import("react").JSX.Element;
@@ -15,6 +15,7 @@ declare const NumberInput: (props: {
15
15
  suffix?: import('react').ReactNode;
16
16
  maxDigits?: number;
17
17
  disabled?: boolean;
18
+ required?: boolean;
18
19
  inputRef?: import('react').Ref<HTMLInputElement>;
19
20
  messages?: Record<string, string | object>;
20
21
  texts?: object;
@@ -17,6 +17,7 @@ declare const SelectInput: (props: {
17
17
  helperText?: string;
18
18
  placeholder?: string;
19
19
  disabled?: boolean;
20
+ required?: boolean;
20
21
  inputRef?: import('react').Ref<HTMLInputElement>;
21
22
  multiple?: boolean;
22
23
  selectAll?: boolean;
@@ -22,7 +22,7 @@
22
22
  * @param {string} [props.autoComplete]
23
23
  * @param {boolean} [props.autoFocus]
24
24
  * @param {boolean} [props.disabled]
25
- * @param {boolean} [props.required]
25
+ * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta y, como en MUI, required en el campo
26
26
  * @param {boolean} [props.fullWidth]
27
27
  * @param {string} [props.id]
28
28
  * @param {object} [props.slotProps]
@@ -20,6 +20,7 @@ declare const TransferListInput: (props: {
20
20
  };
21
21
  helperText?: string;
22
22
  disabled?: boolean;
23
+ required?: boolean;
23
24
  sx?: object;
24
25
  messages?: Record<string, string | object>;
25
26
  texts?: object;
@@ -10,6 +10,7 @@ declare const WizardLayout: (props: {
10
10
  skippedKeys?: Set<string>;
11
11
  isSubmitting?: boolean;
12
12
  orientation?: 'horizontal' | 'vertical';
13
+ hideBackOnFirstStep?: boolean;
13
14
  onBack: () => void;
14
15
  onNext: () => void;
15
16
  onSkip?: () => void;
@@ -1,3 +1,10 @@
1
- export declare const FormSectionContext: import("react").Context<boolean>;
1
+ /** @type {import('react').Context<false|{labelPlacement: string}>} */
2
+ export declare const FormSectionContext: import('react').Context<false | {
3
+ labelPlacement: string;
4
+ }>;
2
5
  /** true si el campo va dentro de una FormSection. */
3
6
  export declare const useIsInsideFormSection: () => boolean;
7
+ /** Cómo pide la sección sus renglones ({labelPlacement}); false fuera de una sección. */
8
+ export declare const useFormSectionLayout: () => false | {
9
+ labelPlacement: string;
10
+ };
@@ -0,0 +1,9 @@
1
+ /** El atributo que marca la etiqueta de un campo obligatorio. */
2
+ export declare const REQUIRED_LABEL_ATTRIBUTE = "data-required";
3
+ /**
4
+ * Los estilos de la etiqueta de un campo obligatorio: la marca después del texto, pegada y del mismo color.
5
+ *
6
+ * @param {string} mark el texto de la marca, ya traducido (common.form.requiredMark: «*»)
7
+ * @returns {object} para el sx de la etiqueta
8
+ */
9
+ export declare function requiredMarkSxOf(mark: string): object;
@@ -134,6 +134,7 @@ declare const _default: Readonly<{
134
134
  showPassword: string;
135
135
  hidePassword: string;
136
136
  clear: string;
137
+ requiredMark: string;
137
138
  selectPlaceholder: string;
138
139
  copy: string;
139
140
  copied: string;
@@ -134,6 +134,7 @@ declare const _default: Readonly<{
134
134
  showPassword: string;
135
135
  hidePassword: string;
136
136
  clear: string;
137
+ requiredMark: string;
137
138
  selectPlaceholder: string;
138
139
  copy: string;
139
140
  copied: string;
@@ -1,7 +1,7 @@
1
1
  /**
2
- * @param {{name: string} & Omit<Parameters<typeof import('./RichTextEditor.js').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'>} props las de
3
- * RichTextEditor (features, maxLength, placeholder…), sin value / onChange / onBlur / error
2
+ * @param {{name: string} & Omit<Parameters<typeof import('./RichTextEditor.js').default>[0], 'name'|'value'|'onChange'|'onBlur'|'error'> & Partial<Pick<Parameters<typeof import('./RichTextEditor.js').default>[0], 'onChange'|'onBlur'>>} props las de
3
+ * RichTextEditor (features, maxLength, placeholder…), sin value ni error (su onChange y su onBlur corren después de los del formulario)
4
4
  */
5
5
  export default function FormRichTextField({ name, ...editorProps }: {
6
6
  name: string;
7
- } & Omit<Parameters<typeof import('./RichTextEditor.js').default>[0], 'name' | 'value' | 'onChange' | 'onBlur' | 'error'>): import("react").JSX.Element;
7
+ } & Omit<Parameters<typeof import('./RichTextEditor.js').default>[0], 'name' | 'value' | 'onChange' | 'onBlur' | 'error'> & Partial<Pick<Parameters<typeof import('./RichTextEditor.js').default>[0], 'onChange' | 'onBlur'>>): import("react").JSX.Element;
@@ -17,6 +17,7 @@ declare const RichTextEditor: (props: {
17
17
  onEditorReady?: (editor: import('@tiptap/react').Editor) => void;
18
18
  minHeight?: number;
19
19
  disabled?: boolean;
20
+ required?: boolean;
20
21
  sx?: object;
21
22
  messages?: Record<string, string | object>;
22
23
  texts?: object;
@@ -16,6 +16,9 @@
16
16
  * consola una vez por idioma y llave
17
17
  * @param {(namespace: string, key: string, values?: object) => string|undefined} [props.translate] traductor del
18
18
  * proyecto (por ejemplo i18next); undefined deja el texto del paquete
19
+ * @param {boolean} [props.requiredFromSchema] los campos de formik leen del esquema de yup del formulario (validationSchema)
20
+ * qué es obligatorio y le ponen su asterisco: el esquema manda y la pantalla no los marca a mano. Lo que la pantalla diga
21
+ * con required gana. Por omisión no (cada campo con su required, como antes)
19
22
  * @param {string} [props.timeZone] zona de las fechas (por omisión la del navegador)
20
23
  * @param {string} [props.currency] moneda de los montos, código ISO 4217 (por omisión MXN)
21
24
  * @param {'h23'|'h12'} [props.hourCycle] la hora de 24 horas («15:10», por omisión) o de 12 con a.m. y p.m. («3:10 p.m.»):
@@ -29,7 +32,7 @@
29
32
  * tiene regla
30
33
  * @param {import('react').ReactNode} props.children
31
34
  */
32
- export declare const ToolkitProvider: ({ language, languages, onLanguageChange, onMissingText, readValidationMessage, texts, translate, timeZone, currency, hourCycle, appName, navigation, buildId, canAccess, fileTypes, children, ...unknownProps }: {
35
+ export declare const ToolkitProvider: ({ language, languages, onLanguageChange, onMissingText, readValidationMessage, requiredFromSchema, texts, translate, timeZone, currency, hourCycle, appName, navigation, buildId, canAccess, fileTypes, children, ...unknownProps }: {
33
36
  language?: string;
34
37
  languages?: Array<{
35
38
  code: string;
@@ -51,6 +54,7 @@ export declare const ToolkitProvider: ({ language, languages, onLanguageChange,
51
54
  language: string;
52
55
  }) => void;
53
56
  translate?: (namespace: string, key: string, values?: object) => string | undefined;
57
+ requiredFromSchema?: boolean;
54
58
  timeZone?: string;
55
59
  currency?: string;
56
60
  hourCycle?: 'h23' | 'h12';
@@ -2,11 +2,12 @@ export declare const ToolkitContext: import("react").Context<null>;
2
2
  /**
3
3
  * La configuración del proveedor; sin proveedor, la vigente con la navegación del navegador.
4
4
  *
5
- * @returns {{language: string, onLanguageChange: Function|null, texts: object, translate: Function|null, timeZone: string, currency: string, hourCycle: 'h23'|'h12', appName: string, buildId: string, navigation: object, canAccess: Function}}
5
+ * @returns {{language: string, onLanguageChange: Function|null, requiredFromSchema?: boolean, texts: object, translate: Function|null, timeZone: string, currency: string, hourCycle: 'h23'|'h12', appName: string, buildId: string, navigation: object, canAccess: Function}}
6
6
  */
7
7
  export declare function useToolkitSettings(): {
8
8
  language: string;
9
9
  onLanguageChange: Function | null;
10
+ requiredFromSchema?: boolean;
10
11
  texts: object;
11
12
  translate: Function | null;
12
13
  timeZone: string;
@@ -19,6 +19,7 @@
19
19
  * @param {boolean} [props.showButton] false esconde «Adjuntar» (la ventana se abre con pickerRef desde un botón suyo)
20
20
  * @param {import('react').Ref<{open: () => void}>} [props.pickerRef] recibe {open} para abrir la ventana de archivos
21
21
  * @param {boolean} [props.disabled]
22
+ * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta (lo revisa el esquema del formulario)
22
23
  * @param {boolean} [props.busy] candado mientras se sube: no se puede agregar ni quitar
23
24
  * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso
24
25
  * @param {Parameters<typeof import('./TemplateDownloadButton.js').default>[0]} [props.template] «Descargar la
@@ -57,6 +58,7 @@ declare function AttachmentsInputContent<T extends File | {
57
58
  open: () => void;
58
59
  }>;
59
60
  disabled?: boolean;
61
+ required?: boolean;
60
62
  busy?: boolean;
61
63
  onReject?: (file: File, reason: string) => void;
62
64
  template?: Parameters<typeof import('./TemplateDownloadButton.js').default>[0];
@@ -17,6 +17,7 @@ declare const FileInput: (props: {
17
17
  buttonLabel?: string;
18
18
  emptyText?: string;
19
19
  disabled?: boolean;
20
+ required?: boolean;
20
21
  busy?: boolean;
21
22
  onReject?: (file: File, reason: string) => void;
22
23
  template?: Parameters<typeof import('./TemplateDownloadButton.js').default>[0];
@@ -40,6 +40,7 @@ declare const FileUploadInput: (props: {
40
40
  };
41
41
  helperText?: string;
42
42
  disabled?: boolean;
43
+ required?: boolean;
43
44
  actions?: import('react').ReactNode;
44
45
  onUploaded?: (result: any) => void;
45
46
  onUploadFailed?: (error: unknown) => void;
@@ -23,6 +23,7 @@ declare const ImageInput: (props: {
23
23
  chooseLabel?: string;
24
24
  removeLabel?: string;
25
25
  disabled?: boolean;
26
+ required?: boolean;
26
27
  busy?: boolean;
27
28
  onReject?: (file: File, reason: string) => void;
28
29
  sx?: object;
@@ -42,6 +42,7 @@ function AttachmentsInputContent(props) {
42
42
  showButton = true,
43
43
  pickerRef,
44
44
  disabled = false,
45
+ required = false,
45
46
  busy = false,
46
47
  onReject,
47
48
  template,
@@ -121,7 +122,7 @@ function AttachmentsInputContent(props) {
121
122
  ]
122
123
  }
123
124
  );
124
- return label ? /* @__PURE__ */ jsx(FieldFrame, { inputId: buttonId, label, children: content }) : content;
125
+ return label ? /* @__PURE__ */ jsx(FieldFrame, { inputId: buttonId, label, required, children: content }) : content;
125
126
  }
126
127
  const AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);
127
128
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"AttachmentsInput.js","sources":["../../src/upload/AttachmentsInput.jsx"],"sourcesContent":["// Varios archivos como en un correo: el botón «Adjuntar» con su clip y cada archivo en un chip con su × (Excel en verde,\n// PDF en rojo…). También se pueden soltar encima. Volver a elegir un archivo con el mismo nombre lo reemplaza (se corrigió en\n// disco) y no cuenta doble.\n//\n// Solo ELIGE (controlado: value / onChange recibe la lista completa), con las reglas de los demás cargadores (accept,\n// maxSize por archivo, maxFiles y maxTotalSize; useFilePicker). La lista puede mezclar archivos nuevos (File) con los que ya\n// están guardados ({id?, name, size?}): al editar un registro, los de antes se ven igual y también se quitan.\n//\n// Huecos: showButton={false} + pickerRef para abrir la ventana desde un botón suyo (el clip de la barra de un correo);\n// template («Descargar la plantilla»); actions (botones suyos junto a «Adjuntar»); onRemove (avisar al API que se quitó uno\n// que ya estaba guardado); label, buttonLabel y texts. Con formik: FormAttachmentsField; con react-hook-form, el del mismo\n// nombre.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAttachmentsInput con defaultProps y\n// styleOverrides de sus partes: root, actions, button, list, fileChip, limits.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useImperativeHandle} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaAttachmentsInput)\nconst THEME_NAME = 'CalcitaAttachmentsInput';\nconst AttachmentsRoot = piecePart(THEME_NAME, 'Root', Box);\nconst AttachmentsActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst AttachmentsButton = piecePart(THEME_NAME, 'Button', Button);\nconst AttachmentsList = piecePart(THEME_NAME, 'List', Stack);\nconst AttachmentsChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst AttachmentsLimits = piecePart(THEME_NAME, 'Limits', Typography);\n\n/**\n * La lista conserva el tipo que usa la pantalla para lo ya guardado (T): onChange regresa esos mismos y los File nuevos.\n *\n * @template {File|{id?: string, name: string, size?: number}} T\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida; sin ella, va sin etiqueta (dentro de un correo, por ejemplo)\n * @param {T[]} [props.value] los nuevos y los que ya están guardados\n * @param {(files: Array<T|File>) => void} props.onChange la lista completa\n * @param {() => void} [props.onBlur] se llama al agregar o quitar (para que el formulario lo marque como tocado)\n * @param {(file: T) => void} [props.onRemove] al quitar uno (antes de onChange)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] el peso máximo de cada archivo: bytes o '10 MB'\n * @param {number} [props.maxFiles] cuántos como máximo\n * @param {number|string} [props.maxTotalSize] cuánto pueden pesar todos juntos\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Adjuntar»\n * @param {boolean} [props.showButton] false esconde «Adjuntar» (la ventana se abre con pickerRef desde un botón suyo)\n * @param {import('react').Ref<{open: () => void}>} [props.pickerRef] recibe {open} para abrir la ventana de archivos\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede agregar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Adjuntar»\n * @param {import('react').ReactNode} [props.actions] botones suyos junto a «Adjuntar»\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 * @param {Record<string, import('react').ElementType>} [props.slots] otro componente en lugar de una parte (root, actions, list…)\n * @param {Record<string, object>} [props.slotProps] props de más para una parte\n */\nfunction AttachmentsInputContent(props) {\n const {\n name, label, value = [], onChange, onBlur, onRemove, accept, maxSize, maxFiles, maxTotalSize, error, helperText, buttonLabel,\n showButton = true, pickerRef, disabled = false, busy = false, onReject, template, actions, 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 {formatFileSize} = useFormatters();\n const generatedId = useId();\n const buttonId = `${name ?? 'attachments'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (files) => {\n onChange(files);\n onBlur?.();\n };\n const picker = useFilePicker({\n accept, maxSize, multiple: true, maxFiles, maxTotalSize, keptFiles: value, disabled: isLocked, onAccept: change, onReject, texts,\n });\n useImperativeHandle(pickerRef, () => ({open: picker.open}), [picker.open]);\n\n const remove = (file) => {\n onRemove?.(file);\n change(value.filter((item) => item !== file));\n };\n const totalBytes = value.reduce((sum, file) => sum + (file.size ?? 0), 0);\n // cuántos y cuánto pesan, solo si hay algún tope que cuidar\n const limitsText = maxFiles || picker.maxTotalBytes\n ? t(picker.maxTotalBytes ? 'upload.summaryOf' : 'upload.summary', {\n count: value.length, total: formatFileSize(totalBytes), max: picker.maxTotalBytes ? formatFileSize(picker.maxTotalBytes) : undefined,\n })\n : null;\n const bottomText = errorText ?? helperText;\n\n const content = (\n <AttachmentsRoot aria-busy={busy} {...picker.dropHandlers}\n ownSx={{\n borderRadius: 1, outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}>\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n {(showButton || template || actions) && (\n <AttachmentsActions direction=\"row\" ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 1}}>\n {showButton && (\n <AttachmentsButton id={buttonId} size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open} disabled={isLocked}>\n {buttonLabel ?? t('upload.attach')}\n </AttachmentsButton>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </AttachmentsActions>\n )}\n {value.length > 0 && (\n <AttachmentsList direction=\"row\" role=\"list\" ownSx={{flexWrap: 'wrap', gap: 1, mt: showButton ? 1 : 0}}>\n {value.map((file) => (\n <Box key={file.id ?? file.name} role=\"listitem\" sx={{maxWidth: '100%'}}>\n <FileChip file={file} ChipComponent={AttachmentsChip} removeLabel={t('upload.remove', {name: file.name})}\n onRemove={isLocked ? undefined : () => remove(file)} />\n </Box>\n ))}\n </AttachmentsList>\n )}\n {limitsText && (\n <AttachmentsLimits variant=\"caption\" color=\"text.secondary\" ownSx={{display: 'block', mt: 0.5}}>{limitsText}</AttachmentsLimits>\n )}\n {bottomText && <FormHelperText error={Boolean(errorText)}>{bottomText}</FormHelperText>}\n </AttachmentsRoot>\n );\n\n return label ? <FieldFrame inputId={buttonId} label={label}>{content}</FieldFrame> : content;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\n// OJO: con su tipo propio, el genérico (T: el tipo de cada archivo) que la envoltura borraría; slots y slotProps van en su\n// documentación\n/** @type {typeof AttachmentsInputContent} */\nconst AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);\nexport default AttachmentsInput;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AttachmentsInput.js","sources":["../../src/upload/AttachmentsInput.jsx"],"sourcesContent":["// Varios archivos como en un correo: el botón «Adjuntar» con su clip y cada archivo en un chip con su × (Excel en verde,\n// PDF en rojo…). También se pueden soltar encima. Volver a elegir un archivo con el mismo nombre lo reemplaza (se corrigió en\n// disco) y no cuenta doble.\n//\n// Solo ELIGE (controlado: value / onChange recibe la lista completa), con las reglas de los demás cargadores (accept,\n// maxSize por archivo, maxFiles y maxTotalSize; useFilePicker). La lista puede mezclar archivos nuevos (File) con los que ya\n// están guardados ({id?, name, size?}): al editar un registro, los de antes se ven igual y también se quitan.\n//\n// Huecos: showButton={false} + pickerRef para abrir la ventana desde un botón suyo (el clip de la barra de un correo);\n// template («Descargar la plantilla»); actions (botones suyos junto a «Adjuntar»); onRemove (avisar al API que se quitó uno\n// que ya estaba guardado); label, buttonLabel y texts. Con formik: FormAttachmentsField; con react-hook-form, el del mismo\n// nombre.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaAttachmentsInput con defaultProps y\n// styleOverrides de sus partes: root, actions, button, list, fileChip, limits.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useImperativeHandle} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaAttachmentsInput)\nconst THEME_NAME = 'CalcitaAttachmentsInput';\nconst AttachmentsRoot = piecePart(THEME_NAME, 'Root', Box);\nconst AttachmentsActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst AttachmentsButton = piecePart(THEME_NAME, 'Button', Button);\nconst AttachmentsList = piecePart(THEME_NAME, 'List', Stack);\nconst AttachmentsChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst AttachmentsLimits = piecePart(THEME_NAME, 'Limits', Typography);\n\n/**\n * La lista conserva el tipo que usa la pantalla para lo ya guardado (T): onChange regresa esos mismos y los File nuevos.\n *\n * @template {File|{id?: string, name: string, size?: number}} T\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida; sin ella, va sin etiqueta (dentro de un correo, por ejemplo)\n * @param {T[]} [props.value] los nuevos y los que ya están guardados\n * @param {(files: Array<T|File>) => void} props.onChange la lista completa\n * @param {() => void} [props.onBlur] se llama al agregar o quitar (para que el formulario lo marque como tocado)\n * @param {(file: T) => void} [props.onRemove] al quitar uno (antes de onChange)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] el peso máximo de cada archivo: bytes o '10 MB'\n * @param {number} [props.maxFiles] cuántos como máximo\n * @param {number|string} [props.maxTotalSize] cuánto pueden pesar todos juntos\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Adjuntar»\n * @param {boolean} [props.showButton] false esconde «Adjuntar» (la ventana se abre con pickerRef desde un botón suyo)\n * @param {import('react').Ref<{open: () => void}>} [props.pickerRef] recibe {open} para abrir la ventana de archivos\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta (lo revisa el esquema del formulario)\n * @param {boolean} [props.busy] candado mientras se sube: no se puede agregar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto a «Adjuntar»\n * @param {import('react').ReactNode} [props.actions] botones suyos junto a «Adjuntar»\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 * @param {Record<string, import('react').ElementType>} [props.slots] otro componente en lugar de una parte (root, actions, list…)\n * @param {Record<string, object>} [props.slotProps] props de más para una parte\n */\nfunction AttachmentsInputContent(props) {\n const {\n name, label, value = [], onChange, onBlur, onRemove, accept, maxSize, maxFiles, maxTotalSize, error, helperText, buttonLabel,\n showButton = true, pickerRef, disabled = false, required = false, busy = false, onReject, template, actions, 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 {formatFileSize} = useFormatters();\n const generatedId = useId();\n const buttonId = `${name ?? 'attachments'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (files) => {\n onChange(files);\n onBlur?.();\n };\n const picker = useFilePicker({\n accept, maxSize, multiple: true, maxFiles, maxTotalSize, keptFiles: value, disabled: isLocked, onAccept: change, onReject, texts,\n });\n useImperativeHandle(pickerRef, () => ({open: picker.open}), [picker.open]);\n\n const remove = (file) => {\n onRemove?.(file);\n change(value.filter((item) => item !== file));\n };\n const totalBytes = value.reduce((sum, file) => sum + (file.size ?? 0), 0);\n // cuántos y cuánto pesan, solo si hay algún tope que cuidar\n const limitsText = maxFiles || picker.maxTotalBytes\n ? t(picker.maxTotalBytes ? 'upload.summaryOf' : 'upload.summary', {\n count: value.length, total: formatFileSize(totalBytes), max: picker.maxTotalBytes ? formatFileSize(picker.maxTotalBytes) : undefined,\n })\n : null;\n const bottomText = errorText ?? helperText;\n\n const content = (\n <AttachmentsRoot aria-busy={busy} {...picker.dropHandlers}\n ownSx={{\n borderRadius: 1, outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}>\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n {(showButton || template || actions) && (\n <AttachmentsActions direction=\"row\" ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 1}}>\n {showButton && (\n <AttachmentsButton id={buttonId} size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open} disabled={isLocked}>\n {buttonLabel ?? t('upload.attach')}\n </AttachmentsButton>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </AttachmentsActions>\n )}\n {value.length > 0 && (\n <AttachmentsList direction=\"row\" role=\"list\" ownSx={{flexWrap: 'wrap', gap: 1, mt: showButton ? 1 : 0}}>\n {value.map((file) => (\n <Box key={file.id ?? file.name} role=\"listitem\" sx={{maxWidth: '100%'}}>\n <FileChip file={file} ChipComponent={AttachmentsChip} removeLabel={t('upload.remove', {name: file.name})}\n onRemove={isLocked ? undefined : () => remove(file)} />\n </Box>\n ))}\n </AttachmentsList>\n )}\n {limitsText && (\n <AttachmentsLimits variant=\"caption\" color=\"text.secondary\" ownSx={{display: 'block', mt: 0.5}}>{limitsText}</AttachmentsLimits>\n )}\n {bottomText && <FormHelperText error={Boolean(errorText)}>{bottomText}</FormHelperText>}\n </AttachmentsRoot>\n );\n\n return label ? <FieldFrame inputId={buttonId} label={label} required={required}>{content}</FieldFrame> : content;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\n// OJO: con su tipo propio, el genérico (T: el tipo de cada archivo) que la envoltura borraría; slots y slotProps van en su\n// documentación\n/** @type {typeof AttachmentsInputContent} */\nconst AttachmentsInput = withPieceSlots(AttachmentsInputContent, THEME_NAME);\nexport default AttachmentsInput;\n"],"names":[],"mappings":"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"}
@@ -34,6 +34,7 @@ function FileInputContent(props) {
34
34
  buttonLabel,
35
35
  emptyText,
36
36
  disabled = false,
37
+ required = false,
37
38
  busy = false,
38
39
  onReject,
39
40
  template,
@@ -55,7 +56,7 @@ function FileInputContent(props) {
55
56
  onBlur?.();
56
57
  };
57
58
  const picker = useFilePicker({ accept, maxSize, disabled: isLocked, onAccept: change, onReject, texts });
58
- return /* @__PURE__ */ jsx(FieldFrame, { inputId: buttonId, label, children: /* @__PURE__ */ jsxs("div", { children: [
59
+ return /* @__PURE__ */ jsx(FieldFrame, { inputId: buttonId, label, required, children: /* @__PURE__ */ jsxs("div", { children: [
59
60
  /* @__PURE__ */ jsxs(
60
61
  FileInputRoot,
61
62
  {
@@ -1 +1 @@
1
- {"version":3,"file":"FileInput.js","sources":["../../src/upload/FileInput.jsx"],"sourcesContent":["// Un archivo del tamaño de un campo, para un formulario (la constancia fiscal de un cliente, el contrato firmado): la\n// etiqueta como los demás campos, el botón «Elegir archivo», el elegido en su chip con la × y abajo su ayuda o su error.\n// Dentro de una FormSection se vuelve renglón, como los demás campos. También se puede soltar el archivo encima.\n//\n// Solo ELIGE (controlado: value / onChange), con las mismas reglas que la zona grande (accept, maxSize; useFilePicker):\n// enviarlo es de la pantalla. value puede ser un archivo nuevo (File) o uno que ya está guardado ({name, size?}), para\n// editar un registro que ya lo tiene.\n//\n// Huecos: template («Descargar la plantilla» junto al botón), actions (botones suyos en el mismo renglón), emptyText,\n// buttonLabel y texts. Con formik: FormFileField (/forms); con react-hook-form, el del mismo nombre (/react-hook-form).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileInput con defaultProps y\n// styleOverrides de sus partes: root, button, fileChip, empty.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileInput)\nconst THEME_NAME = 'CalcitaFileInput';\nconst FileInputRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FileInputButton = piecePart(THEME_NAME, 'Button', Button);\nconst FileInputChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst FileInputEmpty = piecePart(THEME_NAME, 'Empty', Typography);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida\n * @param {File|{name: string, size?: number}|null} [props.value] el archivo nuevo o el que ya está guardado\n * @param {(file: File|null) => void} props.onChange null al quitarlo\n * @param {() => void} [props.onBlur] se llama al elegir o quitar (para que el formulario lo marque como tocado)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Elegir archivo»\n * @param {string} [props.emptyText] ya traducido; por omisión «Ningún archivo elegido»\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.busy] candado mientras se sube: no se puede elegir, soltar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto al botón\n * @param {import('react').ReactNode} [props.actions] botones suyos en el mismo renglón\n * @param {object} [props.sx] ajustes del renglón\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 FileInputContent(props) {\n const {\n name, label, value = null, onChange, onBlur, accept, maxSize, error, helperText = ' ', buttonLabel, emptyText, disabled = false,\n busy = false, onReject, template, actions, 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 buttonId = `${name ?? 'file'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (file) => {\n onChange(file);\n onBlur?.();\n };\n const picker = useFilePicker({accept, maxSize, disabled: isLocked, onAccept: change, onReject, texts});\n\n return (\n <FieldFrame inputId={buttonId} label={label}>\n <div>\n <FileInputRoot\n direction=\"row\"\n aria-busy={busy}\n {...picker.dropHandlers}\n ownSx={{\n alignItems: 'center', flexWrap: 'wrap', gap: 1, minHeight: 40, borderRadius: 1, px: 0.5, mx: -0.5,\n outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}\n >\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n <FileInputButton id={buttonId} variant=\"outlined\" size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open}\n disabled={isLocked} ownSx={{flexShrink: 0}}>\n {buttonLabel ?? t('upload.choose')}\n </FileInputButton>\n {value ? (\n <FileChip file={value} ChipComponent={FileInputChip} removeLabel={t('upload.remove', {name: value.name})}\n onRemove={isLocked ? undefined : () => change(null)} />\n ) : (\n <FileInputEmpty variant=\"body2\" color=\"text.secondary\">{emptyText ?? t('upload.empty')}</FileInputEmpty>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </FileInputRoot>\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 FileInput = withPieceSlots(FileInputContent, THEME_NAME);\nexport default FileInput;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAiCA,MAAM,aAAa;AACnB,MAAM,gBAAgB,UAAU,YAAY,QAAQ,KAAK;AACzD,MAAM,kBAAkB,UAAU,YAAY,UAAU,MAAM;AAC9D,MAAM,gBAAgB,UAAU,YAAY,YAAY,IAAI;AAC5D,MAAM,iBAAiB,UAAU,YAAY,SAAS,UAAU;AA0BhE,SAAS,iBAAiB,OAAO;AAC7B,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO,QAAQ;AAAA,IAAM;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAa;AAAA,IAAW,WAAW;AAAA,IAC1H,OAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAU;AAAA,IAAS;AAAA,IAAI;AAAA,IAAU;AAAA,IACzD,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,WAAW,GAAG,QAAQ,MAAM,IAAI,WAAW;AACjD,QAAM,YAAY,iBAAiB,KAAK;AACxC,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,CAAC,SAAS;AACrB,aAAS,IAAI;AACb,aAAA;AAAA,EACJ;AACA,QAAM,SAAS,cAAc,EAAC,QAAQ,SAAS,UAAU,UAAU,UAAU,QAAQ,UAAU,MAAA,CAAM;AAErG,6BACK,YAAA,EAAW,SAAS,UAAU,OAC3B,+BAAC,OAAA,EACG,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,WAAU;AAAA,QACV,aAAW;AAAA,QACV,GAAG,OAAO;AAAA,QACX,OAAO;AAAA,UACH,YAAY;AAAA,UAAU,UAAU;AAAA,UAAQ,KAAK;AAAA,UAAG,WAAW;AAAA,UAAI,cAAc;AAAA,UAAG,IAAI;AAAA,UAAK,IAAI;AAAA,UAC7F,SAAS,OAAO,aAAa,IAAI;AAAA,UAAG,cAAc;AAAA,UAAU,cAAc;AAAA,UAC1E,SAAS,OAAO,aAAa,iBAAiB;AAAA,QAAA;AAAA,QAElD;AAAA,QAEA,UAAA;AAAA,UAAA,oBAAC,SAAA,EAAO,GAAG,OAAO,YAAY,MAAY,eAAY,QAAO,UAAU,IAAI;AAAA,UAC3E;AAAA,YAAC;AAAA,YAAA;AAAA,cAAgB,IAAI;AAAA,cAAU,SAAQ;AAAA,cAAW,MAAK;AAAA,cAAQ,+BAAY,uBAAA,EAAsB;AAAA,cAAI,SAAS,OAAO;AAAA,cACpG,UAAU;AAAA,cAAU,OAAO,EAAC,YAAY,EAAA;AAAA,cACpD,UAAA,eAAe,EAAE,eAAe;AAAA,YAAA;AAAA,UAAA;AAAA,UAEpC,QACG;AAAA,YAAC;AAAA,YAAA;AAAA,cAAS,MAAM;AAAA,cAAO,eAAe;AAAA,cAAe,aAAa,EAAE,iBAAiB,EAAC,MAAM,MAAM,MAAK;AAAA,cAC7F,UAAU,WAAW,SAAY,MAAM,OAAO,IAAI;AAAA,YAAA;AAAA,UAAA,IAE5D,oBAAC,gBAAA,EAAe,SAAQ,SAAQ,OAAM,kBAAkB,UAAA,aAAa,EAAE,cAAc,EAAA,CAAE;AAAA,UAE1F,YAAY,oBAAC,wBAAA,EAAuB,MAAK,SAAQ,OAAe,GAAG,UAAU;AAAA,UAC7E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,wBAEJ,gBAAA,EAAe,OAAO,QAAQ,SAAS,GAAI,uBAAa,WAAA,CAAW;AAAA,EAAA,EAAA,CACxE,EAAA,CACJ;AAER;AAGA,MAAM,YAAY,eAAe,kBAAkB,UAAU;"}
1
+ {"version":3,"file":"FileInput.js","sources":["../../src/upload/FileInput.jsx"],"sourcesContent":["// Un archivo del tamaño de un campo, para un formulario (la constancia fiscal de un cliente, el contrato firmado): la\n// etiqueta como los demás campos, el botón «Elegir archivo», el elegido en su chip con la × y abajo su ayuda o su error.\n// Dentro de una FormSection se vuelve renglón, como los demás campos. También se puede soltar el archivo encima.\n//\n// Solo ELIGE (controlado: value / onChange), con las mismas reglas que la zona grande (accept, maxSize; useFilePicker):\n// enviarlo es de la pantalla. value puede ser un archivo nuevo (File) o uno que ya está guardado ({name, size?}), para\n// editar un registro que ya lo tiene.\n//\n// Huecos: template («Descargar la plantilla» junto al botón), actions (botones suyos en el mismo renglón), emptyText,\n// buttonLabel y texts. Con formik: FormFileField (/forms); con react-hook-form, el del mismo nombre (/react-hook-form).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileInput con defaultProps y\n// styleOverrides de sus partes: root, button, fileChip, empty.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport FileChip from '@/upload/FileChip.jsx';\nimport TemplateDownloadButton from '@/upload/TemplateDownloadButton.jsx';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileInput)\nconst THEME_NAME = 'CalcitaFileInput';\nconst FileInputRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FileInputButton = piecePart(THEME_NAME, 'Button', Button);\nconst FileInputChip = piecePart(THEME_NAME, 'FileChip', Chip);\nconst FileInputEmpty = piecePart(THEME_NAME, 'Empty', Typography);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida\n * @param {File|{name: string, size?: number}|null} [props.value] el archivo nuevo o el que ya está guardado\n * @param {(file: File|null) => void} props.onChange null al quitarlo\n * @param {() => void} [props.onBlur] se llama al elegir o quitar (para que el formulario lo marque como tocado)\n * @param {string|string[]} [props.accept] 'excel' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {string} [props.buttonLabel] ya traducido; por omisión «Elegir archivo»\n * @param {string} [props.emptyText] ya traducido; por omisión «Ningún archivo elegido»\n * @param {boolean} [props.disabled]\n * @param {boolean} [props.required] obligatorio: el asterisco en la etiqueta (lo revisa el esquema del formulario)\n * @param {boolean} [props.busy] candado mientras se sube: no se puede elegir, soltar ni quitar\n * @param {(file: File, reason: string) => void} [props.onReject] reason es uno de FILE_REJECTION; sin él, un aviso\n * @param {Parameters<typeof import('@/upload/TemplateDownloadButton.jsx').default>[0]} [props.template] «Descargar la\n * plantilla» junto al botón\n * @param {import('react').ReactNode} [props.actions] botones suyos en el mismo renglón\n * @param {object} [props.sx] ajustes del renglón\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 FileInputContent(props) {\n const {\n name, label, value = null, onChange, onBlur, accept, maxSize, error, helperText = ' ', buttonLabel, emptyText, disabled = false, required = false,\n busy = false, onReject, template, actions, 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 buttonId = `${name ?? 'file'}-${generatedId}`;\n const errorText = validationTextOf(error);\n const isLocked = disabled || busy;\n const change = (file) => {\n onChange(file);\n onBlur?.();\n };\n const picker = useFilePicker({accept, maxSize, disabled: isLocked, onAccept: change, onReject, texts});\n\n return (\n <FieldFrame inputId={buttonId} label={label} required={required}>\n <div>\n <FileInputRoot\n direction=\"row\"\n aria-busy={busy}\n {...picker.dropHandlers}\n ownSx={{\n alignItems: 'center', flexWrap: 'wrap', gap: 1, minHeight: 40, borderRadius: 1, px: 0.5, mx: -0.5,\n outline: picker.isDragging ? 2 : 0, outlineStyle: 'dashed', outlineColor: 'primary.main',\n bgcolor: picker.isDragging ? 'action.hover' : 'transparent',\n }}\n sx={sx}\n >\n <input {...picker.inputProps} name={name} aria-hidden=\"true\" tabIndex={-1} />\n <FileInputButton id={buttonId} variant=\"outlined\" size=\"small\" startIcon={<AttachFileRoundedIcon />} onClick={picker.open}\n disabled={isLocked} ownSx={{flexShrink: 0}}>\n {buttonLabel ?? t('upload.choose')}\n </FileInputButton>\n {value ? (\n <FileChip file={value} ChipComponent={FileInputChip} removeLabel={t('upload.remove', {name: value.name})}\n onRemove={isLocked ? undefined : () => change(null)} />\n ) : (\n <FileInputEmpty variant=\"body2\" color=\"text.secondary\">{emptyText ?? t('upload.empty')}</FileInputEmpty>\n )}\n {template && <TemplateDownloadButton size=\"small\" texts={texts} {...template} />}\n {actions}\n </FileInputRoot>\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 FileInput = withPieceSlots(FileInputContent, THEME_NAME);\nexport default FileInput;\n"],"names":[],"mappings":"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"}
@@ -73,6 +73,7 @@ function FileUploadInputContent(props) {
73
73
  error,
74
74
  helperText = " ",
75
75
  disabled = false,
76
+ required = false,
76
77
  actions,
77
78
  onUploaded,
78
79
  onUploadFailed,
@@ -278,7 +279,7 @@ function FileUploadInputContent(props) {
278
279
  if (!label) {
279
280
  return row;
280
281
  }
281
- return /* @__PURE__ */ jsx(FieldFrame, { inputId, labelId, label, children: row });
282
+ return /* @__PURE__ */ jsx(FieldFrame, { inputId, labelId, label, required, children: row });
282
283
  }
283
284
  const FileUploadInput = withPieceSlots(FileUploadInputContent, THEME_NAME);
284
285
  export {