@stackcraftmx/calcita 0.54.0 → 0.56.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 (45) hide show
  1. package/README.md +54 -4
  2. package/dist/api/createApiClient.js +11 -3
  3. package/dist/api/createApiClient.js.map +1 -1
  4. package/dist/appShell/AppLayout.js +5 -1
  5. package/dist/appShell/AppLayout.js.map +1 -1
  6. package/dist/appShell/MobileTabBar.js +12 -1
  7. package/dist/appShell/MobileTabBar.js.map +1 -1
  8. package/dist/appShell/SideMenu.js +3 -1
  9. package/dist/appShell/SideMenu.js.map +1 -1
  10. package/dist/appShell/SideMenuEntry.js +27 -2
  11. package/dist/appShell/SideMenuEntry.js.map +1 -1
  12. package/dist/downloads/downloadQueue.js +3 -1
  13. package/dist/downloads/downloadQueue.js.map +1 -1
  14. package/dist/downloads/downloadsMachine.js +8 -0
  15. package/dist/downloads/downloadsMachine.js.map +1 -1
  16. package/dist/forms/FormWizard.js +22 -17
  17. package/dist/forms/FormWizard.js.map +1 -1
  18. package/dist/hookForm/FormWizard.js +22 -17
  19. package/dist/hookForm/FormWizard.js.map +1 -1
  20. package/dist/index.js +4 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/inputs/FormSection.js +40 -13
  23. package/dist/inputs/FormSection.js.map +1 -1
  24. package/dist/inputs/WizardLayout.js +25 -4
  25. package/dist/inputs/WizardLayout.js.map +1 -1
  26. package/dist/inputs/formLayoutContext.js +1 -1
  27. package/dist/inputs/formLayoutContext.js.map +1 -1
  28. package/dist/inputs/useWizardSteps.js +3 -0
  29. package/dist/inputs/useWizardSteps.js.map +1 -1
  30. package/dist/types/api/createApiClient.d.ts +10 -0
  31. package/dist/types/appShell/AppLayout.d.ts +1 -0
  32. package/dist/types/appShell/MobileTabBar.d.ts +1 -0
  33. package/dist/types/appShell/SideMenu.d.ts +1 -0
  34. package/dist/types/appShell/SideMenuEntry.d.ts +6 -0
  35. package/dist/types/downloads/downloadQueue.d.ts +5 -1
  36. package/dist/types/forms/FormWizard.d.ts +4 -0
  37. package/dist/types/hookForm/FormWizard.d.ts +4 -0
  38. package/dist/types/index.d.ts +2 -1
  39. package/dist/types/inputs/FormSection.d.ts +14 -0
  40. package/dist/types/inputs/WizardLayout.d.ts +1 -0
  41. package/dist/types/inputs/formLayoutContext.d.ts +8 -7
  42. package/dist/types/inputs/useWizardSteps.d.ts +2 -1
  43. package/dist/upload/FileUploadInput.js +1 -1
  44. package/dist/upload/FileUploadInput.js.map +1 -1
  45. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"formLayoutContext.js","sources":["../../src/inputs/formLayoutContext.js"],"sourcesContent":["// Si un campo está dentro de una FormSection (el formulario agrupado de Apple): ahí cada campo se pinta como renglón, con\n// la etiqueta a la izquierda y el valor a la derecha. Fuera de ella, la etiqueta va arriba del campo.\n//\n// El valor es false fuera de una sección (RepeatingFields lo vuelve a poner en false para sus renglones) y, dentro, cómo\n// pide la sección sus renglones: {labelPlacement}.\n//\n// Va en su propio archivo y no junto a FormSection: FieldFrame lo lee, y así FieldFrame no importa a FormSection (que a su\n// vez usa FormRow).\nimport {createContext, useContext} from 'react';\n\n/** @type {import('react').Context<false|{labelPlacement: string}>} */\nexport const FormSectionContext = createContext(/** @type {false|{labelPlacement: string}} */ (false));\n\n/** true si el campo va dentro de una FormSection. */\nexport const useIsInsideFormSection = () => Boolean(useContext(FormSectionContext));\n\n/** Cómo pide la sección sus renglones ({labelPlacement}); false fuera de una sección. */\nexport const useFormSectionLayout = () => useContext(FormSectionContext);\n"],"names":[],"mappings":";;AAWY,MAAC,qBAAqB;AAAA;AAAA,EAA6D;AAAK;AAGxF,MAAC,yBAAyB,MAAM,QAAQ,WAAW,kBAAkB,CAAC;AAGtE,MAAC,uBAAuB,MAAM,WAAW,kBAAkB;"}
1
+ {"version":3,"file":"formLayoutContext.js","sources":["../../src/inputs/formLayoutContext.js"],"sourcesContent":["// Si un campo está dentro de una FormSection (el formulario agrupado de Apple): ahí cada campo se pinta como renglón, con\n// la etiqueta a la izquierda y el valor a la derecha. Fuera de ella, la etiqueta va arriba del campo.\n//\n// El valor es false fuera de una sección (RepeatingFields lo vuelve a poner en false para sus renglones) y, dentro, cómo\n// pide la sección sus renglones: {labelPlacement, labelWidth}.\n//\n// Va en su propio archivo y no junto a FormSection: FieldFrame lo lee, y así FieldFrame no importa a FormSection (que a su\n// vez usa FormRow).\nimport {createContext, useContext} from 'react';\n\n/** @typedef {{labelPlacement: string, labelWidth?: string|number}} FormSectionLayout */\n\n/** @type {import('react').Context<false|FormSectionLayout>} */\nexport const FormSectionContext = createContext(/** @type {false|FormSectionLayout} */ (false));\n\n/** true si el campo va dentro de una FormSection. */\nexport const useIsInsideFormSection = () => Boolean(useContext(FormSectionContext));\n\n/** Cómo pide la sección sus renglones ({labelPlacement, labelWidth}); false fuera de una sección. */\nexport const useFormSectionLayout = () => useContext(FormSectionContext);\n"],"names":[],"mappings":";;AAaY,MAAC,qBAAqB;AAAA;AAAA,EAAsD;AAAK;AAGjF,MAAC,yBAAyB,MAAM,QAAQ,WAAW,kBAAkB,CAAC;AAGtE,MAAC,uBAAuB,MAAM,WAAW,kBAAkB;"}
@@ -26,6 +26,9 @@ function useWizardSteps({ steps, initialStepKey, stepSelection = WIZARD_STEP_SEL
26
26
  if (action === "checkFirst" && !await isStepValid(activeIndex)) {
27
27
  return;
28
28
  }
29
+ if (action === "checkFirst" && index > activeIndex && steps[activeIndex].nextDisabledReason) {
30
+ return;
31
+ }
29
32
  setActiveIndex(index);
30
33
  };
31
34
  return { activeIndex, setActiveIndex, selectStep: stepSelection === WIZARD_STEP_SELECTION.none ? void 0 : selectStep };
@@ -1 +1 @@
1
- {"version":3,"file":"useWizardSteps.js","sources":["../../src/inputs/useWizardSteps.js"],"sourcesContent":["// Lo que los dos FormWizard (formik y react-hook-form) hacen igual con el paso abierto: en qué paso empieza, avisar hacia\n// afuera cada vez que cambia y qué pasa al pulsar el título de otro paso. Lo de cada librería (cómo se revisa un paso) llega\n// en isStepValid.\n//\n// Hook genérico: function (regla de la casa).\nimport {useEffect, useRef, useState} from 'react';\n\nimport {WIZARD_STEP_SELECTION, openingStepOf, stepSelectionActionOf} from '@/inputs/wizardSteps.js';\nimport {createLogger} from '@/observability/logger.js';\n\nconst logger = createLogger('FormWizard');\n\n/**\n * @param {object} options\n * @param {Array<{key: string}>} options.steps\n * @param {string} [options.initialStepKey] el paso en el que se abre (el que diga el servidor); por omisión el primero\n * @param {string} [options.stepSelection] uno de WIZARD_STEP_SELECTION; por omisión none\n * @param {(stepKey: string, index: number) => void} [options.onStepChange] cada vez que cambia el paso abierto\n * @param {(index: number) => Promise<boolean>} options.isStepValid revisa los campos de ese paso y marca sus errores\n * @returns {{activeIndex: number, setActiveIndex: import('react').Dispatch<import('react').SetStateAction<number>>, selectStep: ((index: number) => Promise<void>)|undefined}}\n * selectStep es undefined con none: así WizardLayout sabe que los títulos no se pulsan\n */\nexport function useWizardSteps({steps, initialStepKey, stepSelection = WIZARD_STEP_SELECTION.none, onStepChange, isStepValid}) {\n const [opening] = useState(() => openingStepOf(steps, initialStepKey));\n const [activeIndex, setActiveIndex] = useState(opening.index);\n const announcedIndex = useRef(opening.index);\n\n // una llave que no es de ningún paso no tumba la pantalla: se abre el primero y se dice\n useEffect(() => {\n if (opening.isUnknownKey) {\n logger.warn(`initialStepKey «${initialStepKey}» no es de ningún paso: se abrió el primero`, {stepKeys: steps.map((step) => step.key)});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // hacia afuera, solo cuando de veras cambia (no al abrir): la pantalla puede guardar el paso en la dirección\n useEffect(() => {\n if (announcedIndex.current !== activeIndex) {\n announcedIndex.current = activeIndex;\n onStepChange?.(steps[activeIndex].key, activeIndex);\n }\n }, [activeIndex, onStepChange, steps]);\n\n const selectStep = async (index) => {\n const action = stepSelectionActionOf(stepSelection, activeIndex, index);\n if (action === 'ignore') {\n return;\n }\n // con checked, un paso con errores no se deja: se queda ahí para que se corrijan\n if (action === 'checkFirst' && !(await isStepValid(activeIndex))) {\n return;\n }\n setActiveIndex(index);\n };\n\n return {activeIndex, setActiveIndex, selectStep: stepSelection === WIZARD_STEP_SELECTION.none ? undefined : selectStep};\n}\n"],"names":[],"mappings":";;;;AAUA,MAAM,SAAS,aAAa,YAAY;AAYjC,SAAS,eAAe,EAAC,OAAO,gBAAgB,gBAAgB,sBAAsB,MAAM,cAAc,YAAW,GAAG;AAC3H,QAAM,CAAC,OAAO,IAAI,SAAS,MAAM,cAAc,OAAO,cAAc,CAAC;AACrE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,QAAQ,KAAK;AAC5D,QAAM,iBAAiB,OAAO,QAAQ,KAAK;AAG3C,YAAU,MAAM;AACZ,QAAI,QAAQ,cAAc;AACtB,aAAO,KAAK,mBAAmB,cAAc,+CAA+C,EAAC,UAAU,MAAM,IAAI,CAAC,SAAS,KAAK,GAAG,EAAC,CAAC;AAAA,IACzI;AAAA,EAEJ,GAAG,CAAA,CAAE;AAGL,YAAU,MAAM;AACZ,QAAI,eAAe,YAAY,aAAa;AACxC,qBAAe,UAAU;AACzB,qBAAe,MAAM,WAAW,EAAE,KAAK,WAAW;AAAA,IACtD;AAAA,EACJ,GAAG,CAAC,aAAa,cAAc,KAAK,CAAC;AAErC,QAAM,aAAa,OAAO,UAAU;AAChC,UAAM,SAAS,sBAAsB,eAAe,aAAa,KAAK;AACtE,QAAI,WAAW,UAAU;AACrB;AAAA,IACJ;AAEA,QAAI,WAAW,gBAAgB,CAAE,MAAM,YAAY,WAAW,GAAI;AAC9D;AAAA,IACJ;AACA,mBAAe,KAAK;AAAA,EACxB;AAEA,SAAO,EAAC,aAAa,gBAAgB,YAAY,kBAAkB,sBAAsB,OAAO,SAAY,WAAU;AAC1H;"}
1
+ {"version":3,"file":"useWizardSteps.js","sources":["../../src/inputs/useWizardSteps.js"],"sourcesContent":["// Lo que los dos FormWizard (formik y react-hook-form) hacen igual con el paso abierto: en qué paso empieza, avisar hacia\n// afuera cada vez que cambia y qué pasa al pulsar el título de otro paso. Lo de cada librería (cómo se revisa un paso) llega\n// en isStepValid.\n//\n// Hook genérico: function (regla de la casa).\nimport {useEffect, useRef, useState} from 'react';\n\nimport {WIZARD_STEP_SELECTION, openingStepOf, stepSelectionActionOf} from '@/inputs/wizardSteps.js';\nimport {createLogger} from '@/observability/logger.js';\n\nconst logger = createLogger('FormWizard');\n\n/**\n * @param {object} options\n * @param {Array<{key: string, nextDisabledReason?: unknown}>} options.steps\n * @param {string} [options.initialStepKey] el paso en el que se abre (el que diga el servidor); por omisión el primero\n * @param {string} [options.stepSelection] uno de WIZARD_STEP_SELECTION; por omisión none\n * @param {(stepKey: string, index: number) => void} [options.onStepChange] cada vez que cambia el paso abierto\n * @param {(index: number) => Promise<boolean>} options.isStepValid revisa los campos de ese paso y marca sus errores\n * @returns {{activeIndex: number, setActiveIndex: import('react').Dispatch<import('react').SetStateAction<number>>, selectStep: ((index: number) => Promise<void>)|undefined}}\n * selectStep es undefined con none: así WizardLayout sabe que los títulos no se pulsan\n */\nexport function useWizardSteps({steps, initialStepKey, stepSelection = WIZARD_STEP_SELECTION.none, onStepChange, isStepValid}) {\n const [opening] = useState(() => openingStepOf(steps, initialStepKey));\n const [activeIndex, setActiveIndex] = useState(opening.index);\n const announcedIndex = useRef(opening.index);\n\n // una llave que no es de ningún paso no tumba la pantalla: se abre el primero y se dice\n useEffect(() => {\n if (opening.isUnknownKey) {\n logger.warn(`initialStepKey «${initialStepKey}» no es de ningún paso: se abrió el primero`, {stepKeys: steps.map((step) => step.key)});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // hacia afuera, solo cuando de veras cambia (no al abrir): la pantalla puede guardar el paso en la dirección\n useEffect(() => {\n if (announcedIndex.current !== activeIndex) {\n announcedIndex.current = activeIndex;\n onStepChange?.(steps[activeIndex].key, activeIndex);\n }\n }, [activeIndex, onStepChange, steps]);\n\n const selectStep = async (index) => {\n const action = stepSelectionActionOf(stepSelection, activeIndex, index);\n if (action === 'ignore') {\n return;\n }\n // con checked, un paso con errores no se deja: se queda ahí para que se corrijan\n if (action === 'checkFirst' && !(await isStepValid(activeIndex))) {\n return;\n }\n // con checked, un paso que todavía no se puede dejar (su nextDisabledReason) tampoco deja saltar hacia adelante; hacia\n // atrás sí: no le falta nada a lo de antes\n if (action === 'checkFirst' && index > activeIndex && steps[activeIndex].nextDisabledReason) {\n return;\n }\n setActiveIndex(index);\n };\n\n return {activeIndex, setActiveIndex, selectStep: stepSelection === WIZARD_STEP_SELECTION.none ? undefined : selectStep};\n}\n"],"names":[],"mappings":";;;;AAUA,MAAM,SAAS,aAAa,YAAY;AAYjC,SAAS,eAAe,EAAC,OAAO,gBAAgB,gBAAgB,sBAAsB,MAAM,cAAc,YAAW,GAAG;AAC3H,QAAM,CAAC,OAAO,IAAI,SAAS,MAAM,cAAc,OAAO,cAAc,CAAC;AACrE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,QAAQ,KAAK;AAC5D,QAAM,iBAAiB,OAAO,QAAQ,KAAK;AAG3C,YAAU,MAAM;AACZ,QAAI,QAAQ,cAAc;AACtB,aAAO,KAAK,mBAAmB,cAAc,+CAA+C,EAAC,UAAU,MAAM,IAAI,CAAC,SAAS,KAAK,GAAG,EAAC,CAAC;AAAA,IACzI;AAAA,EAEJ,GAAG,CAAA,CAAE;AAGL,YAAU,MAAM;AACZ,QAAI,eAAe,YAAY,aAAa;AACxC,qBAAe,UAAU;AACzB,qBAAe,MAAM,WAAW,EAAE,KAAK,WAAW;AAAA,IACtD;AAAA,EACJ,GAAG,CAAC,aAAa,cAAc,KAAK,CAAC;AAErC,QAAM,aAAa,OAAO,UAAU;AAChC,UAAM,SAAS,sBAAsB,eAAe,aAAa,KAAK;AACtE,QAAI,WAAW,UAAU;AACrB;AAAA,IACJ;AAEA,QAAI,WAAW,gBAAgB,CAAE,MAAM,YAAY,WAAW,GAAI;AAC9D;AAAA,IACJ;AAGA,QAAI,WAAW,gBAAgB,QAAQ,eAAe,MAAM,WAAW,EAAE,oBAAoB;AACzF;AAAA,IACJ;AACA,mBAAe,KAAK;AAAA,EACxB;AAEA,SAAO,EAAC,aAAa,gBAAgB,YAAY,kBAAkB,sBAAsB,OAAO,SAAY,WAAU;AAC1H;"}
@@ -67,6 +67,14 @@ export type ApiRequestOptions = {
67
67
  timeoutMilliseconds?: number;
68
68
  };
69
69
  export type ApiDownloadOptions = {
70
+ /**
71
+ * por omisión 'GET'; 'POST' para un archivo que se arma con lo que va en `body`
72
+ */
73
+ method?: string;
74
+ /**
75
+ * lo que va en la petición, como en post: objeto → JSON; FormData, Blob o URLSearchParams tal cual
76
+ */
77
+ body?: unknown;
70
78
  params?: Record<string, unknown>;
71
79
  signal?: AbortSignal;
72
80
  onDownloadProgress?: (progress: {
@@ -110,6 +118,8 @@ export type ApiClient = {
110
118
  * Lo que acepta una descarga.
111
119
  *
112
120
  * @typedef {object} ApiDownloadOptions
121
+ * @property {string} [method] por omisión 'GET'; 'POST' para un archivo que se arma con lo que va en `body`
122
+ * @property {unknown} [body] lo que va en la petición, como en post: objeto → JSON; FormData, Blob o URLSearchParams tal cual
113
123
  * @property {Record<string, unknown>} [params]
114
124
  * @property {AbortSignal} [signal]
115
125
  * @property {(progress: {loaded: number, total: number|null}) => void} [onDownloadProgress]
@@ -36,6 +36,7 @@ export declare const AppLayout: (props: {
36
36
  defaultCollapsed?: boolean;
37
37
  minimizeTabBarOnScroll?: boolean;
38
38
  contentMaxWidth?: number | string;
39
+ menuLabelOverflow?: string;
39
40
  onMenuCollapse?: (collapsed: boolean) => void;
40
41
  onMobileMenuChange?: (isOpen: boolean) => void;
41
42
  onMobileTabsChange?: (keys: string[] | null) => void;
@@ -5,6 +5,7 @@ declare const MobileTabBar: (props: {
5
5
  account?: import('react').ReactNode;
6
6
  moreFooter?: import('react').ReactNode;
7
7
  minimizeOnScroll?: boolean;
8
+ labelOverflow?: string;
8
9
  onPinnedTabsChange?: (keys: string[] | null) => void;
9
10
  } & {
10
11
  slots?: Record<string, import('react').ElementType>;
@@ -16,6 +16,7 @@ declare const SideMenu: (props: {
16
16
  menuBadges?: Record<string, number>;
17
17
  railTop?: import('react').ReactNode;
18
18
  railBottom?: import('react').ReactNode;
19
+ labelOverflow?: string;
19
20
  } & {
20
21
  slots?: Record<string, import('react').ElementType>;
21
22
  slotProps?: Record<string, object | ((partProps: object) => object)>;
@@ -1,9 +1,15 @@
1
+ /** Qué hace el menú lateral con un nombre que no cabe en su renglón. */
2
+ export declare const MENU_LABEL_OVERFLOW: Readonly<{
3
+ ellipsis: "ellipsis";
4
+ wrap: "wrap";
5
+ }>;
1
6
  declare const SideMenuEntry: (props: {
2
7
  menuItem: object;
3
8
  activeItem: object | null;
4
9
  collapsed: boolean;
5
10
  onNavigate?: () => void;
6
11
  menuBadges?: Record<string, number>;
12
+ labelOverflow?: string;
7
13
  depth?: number;
8
14
  } & {
9
15
  slots?: Record<string, import('react').ElementType>;
@@ -9,7 +9,10 @@ export declare const downloadsActor: import("xstate").Actor<import("xstate").Sta
9
9
  * archivo
10
10
  * @param {string} [options.format] el tipo de archivo, para su ícono en el panel: xlsx, csv, pdf o uno del proyecto
11
11
  * (ToolkitProvider fileTypes)
12
- * @param {(transferOptions: {signal: AbortSignal, onDownloadProgress: (progress: {loaded: number, total?: number|null}) => void}) => Promise<{blob: Blob, fileName: string}>} options.request
12
+ * @param {(transferOptions: {signal: AbortSignal, onDownloadProgress: (progress: {loaded: number, total?: number|null}) => void, onLabelChange: (label: string) => void}) => Promise<{blob: Blob, fileName: string}>} options.request
13
+ * baja el archivo y regresa {blob, fileName}. onLabelChange es para un lote de varios archivos que cuenta como una
14
+ * sola descarga: cambia el rótulo del panel mientras baja («3 de 11: contrato.pdf»); quien no lo llame se queda con
15
+ * `label`
13
16
  * @param {(fileName: string) => void} [options.onCompleted] p. ej. volver a pedir la tabla (si la descarga cambia algo
14
17
  * en el servidor)
15
18
  * @param {(error: unknown) => void} [options.onFailed] cuando no se pudo (el panel ya lo dice; para avisar o reportarlo)
@@ -25,6 +28,7 @@ export declare function startDownload({ label, format, request, onCompleted, onF
25
28
  loaded: number;
26
29
  total?: number | null;
27
30
  }) => void;
31
+ onLabelChange: (label: string) => void;
28
32
  }) => Promise<{
29
33
  blob: Blob;
30
34
  fileName: string;
@@ -5,6 +5,9 @@
5
5
  * @param {string} props.steps[].label ya traducido
6
6
  * @param {string[]} [props.steps[].fields] los campos del paso (los que revisa «Siguiente»)
7
7
  * @param {boolean} [props.steps[].optional] se puede omitir
8
+ * @param {import('react').ReactNode} [props.steps[].nextDisabledReason] ya traducido: el paso todavía no se puede dejar («Faltan 2
9
+ * documentos»). «Siguiente» se ve apagado con el motivo en un aviso (escrito, en el teléfono); en el último paso se apaga
10
+ * el de enviar. Con stepSelection checked tampoco se salta a un paso de adelante. Sin él, como siempre
8
11
  * @param {import('react').ReactNode} props.steps[].content los campos del paso
9
12
  * @param {boolean} [props.steps[].completed] lleva la palomita (cuando los títulos se pulsan, solo si la pantalla lo dice)
10
13
  * @param {string} props.submitLabel ya traducido: el botón del último paso
@@ -22,6 +25,7 @@ export default function FormWizard({ steps, submitLabel, orientation, hideBackOn
22
25
  label: string;
23
26
  fields?: string[];
24
27
  optional?: boolean;
28
+ nextDisabledReason?: import('react').ReactNode;
25
29
  content: import('react').ReactNode;
26
30
  completed?: boolean;
27
31
  }[];
@@ -5,6 +5,9 @@
5
5
  * @param {string} props.steps[].label ya traducido
6
6
  * @param {string[]} [props.steps[].fields] los campos del paso (los que revisa «Siguiente»)
7
7
  * @param {boolean} [props.steps[].optional] se puede omitir
8
+ * @param {import('react').ReactNode} [props.steps[].nextDisabledReason] ya traducido: el paso todavía no se puede dejar («Faltan 2
9
+ * documentos»). «Siguiente» se ve apagado con el motivo en un aviso (escrito, en el teléfono); en el último paso se apaga
10
+ * el de enviar. Con stepSelection checked tampoco se salta a un paso de adelante. Sin él, como siempre
8
11
  * @param {import('react').ReactNode} props.steps[].content los campos del paso
9
12
  * @param {boolean} [props.steps[].completed] lleva la palomita (cuando los títulos se pulsan, solo si la pantalla lo dice)
10
13
  * @param {string} props.submitLabel ya traducido: el botón del último paso
@@ -22,6 +25,7 @@ export default function FormWizard({ steps, submitLabel, orientation, hideBackOn
22
25
  label: string;
23
26
  fields?: string[];
24
27
  optional?: boolean;
28
+ nextDisabledReason?: import('react').ReactNode;
25
29
  content: import('react').ReactNode;
26
30
  completed?: boolean;
27
31
  }[];
@@ -24,6 +24,7 @@ export { parseMarkedText } from './ui/labels/markedTextPieces.js';
24
24
  export { CSS_TOKENS, cssTokenDeclarationsOf, cssTokenOf } from './theme/cssTokens.js';
25
25
  export { GLASS_INSET_PIXELS, GLASS_MENU_RADIUS_PIXELS, GLASS_OPACITY, GLASS_PANEL_RADIUS_PIXELS, ambientBackgroundOf, glassDropOf, glassStylesOf, glassSurfaceOf, FIELD_TONES, accentTextStylesOf, canvasFieldStylesOf, fieldPaletteOf, fieldWellStylesOf, fixedBackgroundLayerOf, isGlassTheme, materialBackgroundOf, materialStylesOf, materialSurfaceOf, scrollEdgeStylesOf, } from './theme/glassSurface.js';
26
26
  export { AppLayout, MENU_POSITION } from './appShell/AppLayout.js';
27
+ export { MENU_LABEL_OVERFLOW } from './appShell/SideMenuEntry.js';
27
28
  export { AuthLayout } from './appShell/AuthLayout.js';
28
29
  export { default as BarActions } from './appShell/BarActions.js';
29
30
  export { default as BarActionButton } from './appShell/BarActionButton.js';
@@ -69,7 +70,7 @@ export { default as ColorInput } from './inputs/ColorInput.js';
69
70
  export { default as FieldFrame } from './inputs/FieldFrame.js';
70
71
  export { default as MaskedInput } from './inputs/MaskedInput.js';
71
72
  export { INPUT_MASK, isMaskComplete, maskPatternOf, maskedTextOf, rawValueOf } from './inputs/inputMasks.js';
72
- export { FORM_LABEL_PLACEMENT, FORM_ROW_CLASS, FormRow, default as FormSection } from './inputs/FormSection.js';
73
+ export { FORM_LABEL_PLACEMENT, FORM_LABEL_WIDTH, FORM_ROW_CLASS, FormRow, default as FormSection } from './inputs/FormSection.js';
73
74
  export { default as CheckboxGroupInput } from './inputs/CheckboxGroupInput.js';
74
75
  export { default as NumberInput } from './inputs/NumberInput.js';
75
76
  export { default as TransferListInput } from './inputs/TransferListInput.js';
@@ -6,6 +6,13 @@ export declare const FORM_LABEL_PLACEMENT: Readonly<{
6
6
  side: "side";
7
7
  topOnPhone: "topOnPhone";
8
8
  }>;
9
+ /**
10
+ * Lo ancho de la columna de las etiquetas que no es un número: auto = la etiqueta se lleva lo que sobre y el campo solo lo
11
+ * que ocupa (una lista de documentos con su cargador).
12
+ */
13
+ export declare const FORM_LABEL_WIDTH: Readonly<{
14
+ auto: "auto";
15
+ }>;
9
16
  /** La clase de cada renglón: la sección le dibuja su línea de arriba, esté donde esté adentro de la tarjeta. */
10
17
  export declare const FORM_ROW_CLASS = "calcita-form-row";
11
18
  /**
@@ -32,11 +39,18 @@ export declare function FormRow(props: {
32
39
  * @param {import('react').ReactNode} [props.footer] la explicación, abajo de la tarjeta
33
40
  * @param {string} [props.labelPlacement] uno de FORM_LABEL_PLACEMENT: side (por omisión, la etiqueta a la izquierda) o
34
41
  * topOnPhone (arriba del campo por debajo de `sm`)
42
+ * @param {'auto'|string|number} [props.labelWidth] lo ancho de la columna de las etiquetas: FORM_LABEL_WIDTH.auto (la etiqueta
43
+ * se lleva lo que sobre y el campo lo que ocupa), un ancho de CSS ('320px', '50%') o un número en px; por omisión el 38 %
44
+ * del renglón hasta 200 px, como siempre
45
+ * @param {import('@mui/material/styles').SxProps<import('@mui/material/styles').Theme>} [props.sx] los estilos de la sección
46
+ * en esta pantalla, encima de los del tema (como en las demás piezas)
35
47
  * @param {import('react').ReactNode} props.children los campos, directo o dentro de un Grid, un Stack o un componente propio
36
48
  */
37
49
  export default function FormSection(props: {
38
50
  title?: string;
39
51
  footer?: import('react').ReactNode;
40
52
  labelPlacement?: string;
53
+ labelWidth?: 'auto' | string | number;
54
+ sx?: import('@mui/material/styles').SxProps<import('@mui/material/styles').Theme>;
41
55
  children: import('react').ReactNode;
42
56
  }): import("react").JSX.Element;
@@ -9,6 +9,7 @@ declare const WizardLayout: (props: {
9
9
  activeIndex: number;
10
10
  skippedKeys?: Set<string>;
11
11
  isSubmitting?: boolean;
12
+ nextDisabledReason?: import('react').ReactNode;
12
13
  orientation?: 'horizontal' | 'vertical';
13
14
  hideBackOnFirstStep?: boolean;
14
15
  onBack: () => void;
@@ -1,10 +1,11 @@
1
- /** @type {import('react').Context<false|{labelPlacement: string}>} */
2
- export declare const FormSectionContext: import('react').Context<false | {
1
+ export type FormSectionLayout = {
3
2
  labelPlacement: string;
4
- }>;
3
+ labelWidth?: string | number;
4
+ };
5
+ /** @typedef {{labelPlacement: string, labelWidth?: string|number}} FormSectionLayout */
6
+ /** @type {import('react').Context<false|FormSectionLayout>} */
7
+ export declare const FormSectionContext: import('react').Context<false | FormSectionLayout>;
5
8
  /** true si el campo va dentro de una FormSection. */
6
9
  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
- };
10
+ /** Cómo pide la sección sus renglones ({labelPlacement, labelWidth}); false fuera de una sección. */
11
+ export declare const useFormSectionLayout: () => false | FormSectionLayout;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @param {object} options
3
- * @param {Array<{key: string}>} options.steps
3
+ * @param {Array<{key: string, nextDisabledReason?: unknown}>} options.steps
4
4
  * @param {string} [options.initialStepKey] el paso en el que se abre (el que diga el servidor); por omisión el primero
5
5
  * @param {string} [options.stepSelection] uno de WIZARD_STEP_SELECTION; por omisión none
6
6
  * @param {(stepKey: string, index: number) => void} [options.onStepChange] cada vez que cambia el paso abierto
@@ -11,6 +11,7 @@
11
11
  export declare function useWizardSteps({ steps, initialStepKey, stepSelection, onStepChange, isStepValid }: {
12
12
  steps: Array<{
13
13
  key: string;
14
+ nextDisabledReason?: unknown;
14
15
  }>;
15
16
  initialStepKey?: string;
16
17
  stepSelection?: string;
@@ -271,7 +271,7 @@ function FileUploadInputContent(props) {
271
271
  {
272
272
  error: Boolean(errorText),
273
273
  role: errorText ? "alert" : progressText ? "status" : void 0,
274
- ownSx: { textAlign: "right", mx: 0 },
274
+ ownSx: { textAlign: "right", mx: 0, width: 0, minWidth: "100%" },
275
275
  children: errorText ?? progressText ?? helperText
276
276
  }
277
277
  )
@@ -1 +1 @@
1
- {"version":3,"file":"FileUploadInput.js","sources":["../../src/upload/FileUploadInput.jsx"],"sourcesContent":["// El cargador MINI: un archivo que se SUBE en cuanto se elige, del tamaño de un renglón. Para una lista de documentos (el\n// expediente de un cliente, lo que pide una solicitud): a la izquierda el nombre del documento (la etiqueta del renglón) y a\n// la derecha el nombre del archivo, su estado y el botón.\n//\n// estado Por cargar · Subiendo 40 % · Procesando · Cargado · No se cargó\n// botón Cargar (lleno) → ya cargado, Sustituir (con borde); mientras sube, Cancelar\n// íconos bajar el que ya subió (si la pantalla da download) y quitarlo\n// abajo el avance en palabras («1.2 MB de 3 MB · 350 kB/s · faltan 5 segundos»; el porcentaje ya va en el estado) o el\n// motivo del error\n//\n// Al formulario llega SOLO lo que el servidor da para reconocer el archivo (su id, por omisión result.id): el formulario\n// manda ese id junto con lo demás y el backend lo liga a su registro. Mientras sube, el valor es '' (no se puede guardar\n// con el id del anterior). Subir es de la pantalla: `upload` recibe el archivo con su señal para cancelar, su avance y una\n// llave por archivo elegido (si la red se cae a media subida, el servidor no deja dos copias). Con uploadWithProgress o\n// con axios, igual.\n//\n// Lo que es del negocio (Aprobado, Falta información) no va aquí: la pantalla lo pinta junto, con `actions`, y sabe en\n// qué va cada uno con onStatusChange (para «3 de 5 cargados»). Tipo y peso se revisan ANTES de subir (accept, maxSize): no\n// se gastan minutos subiendo lo que el servidor va a rechazar; el motivo sale abajo, en rojo.\n//\n// Dentro de una FormSection se vuelve renglón, como los demás campos. También se puede soltar el archivo encima.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileUploadInput con defaultProps y\n// styleOverrides de sus partes: root, fileName, status, button, helper.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {useMachine} from '@xstate/react';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\nimport FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';\nimport FileUploadOutlinedIcon from '@mui/icons-material/FileUploadOutlined';\n\nimport {startDownload} from '@/downloads/downloadQueue.js';\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport {newIdentifier} from '@/identifiers.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {fieldErrorsOf} from '@/problems.js';\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 {FILE_REJECTION} from '@/upload/fileRules.js';\nimport {transferProgressOf} from '@/upload/transferProgress.js';\nimport {buildUploadActors, uploadMachine} from '@/upload/uploadMachine.js';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n/** En qué va el archivo de un renglón (onStatusChange). */\nexport const FILE_UPLOAD_STATUS = Object.freeze({\n pending: 'pending', uploading: 'uploading', processing: 'processing', uploaded: 'uploaded', failed: 'failed',\n});\n\n// el color de la etiqueta de cada estado (el color es información)\nconst STATUS_COLORS = Object.freeze({\n [FILE_UPLOAD_STATUS.pending]: 'default',\n [FILE_UPLOAD_STATUS.uploading]: 'info',\n [FILE_UPLOAD_STATUS.processing]: 'info',\n [FILE_UPLOAD_STATUS.uploaded]: 'success',\n [FILE_UPLOAD_STATUS.failed]: 'error',\n});\n\n// entre las partes del avance en palabras\nconst PROGRESS_SEPARATOR = ' · ';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileUploadInput)\nconst THEME_NAME = 'CalcitaFileUploadInput';\nconst FileUploadRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FileUploadName = piecePart(THEME_NAME, 'FileName', Typography);\nconst FileUploadStatus = piecePart(THEME_NAME, 'Status', Chip);\nconst FileUploadButton = piecePart(THEME_NAME, 'Button', Button);\nconst FileUploadHelper = piecePart(THEME_NAME, 'Helper', FormHelperText);\n\n/**\n * El texto de un error de la subida: el del campo si el servidor mandó un 422 con su lista, si no su detail, y si no hubo\n * respuesta, el genérico.\n *\n * @param {unknown} error\n * @returns {string|null}\n */\nfunction uploadErrorTextOf(error) {\n const problem = /** @type {{problem?: object, message?: string}} */ (error)?.problem;\n const firstFieldMessage = Object.values(fieldErrorsOf(problem ?? null)).flat()[0];\n return firstFieldMessage ?? problem?.detail ?? null;\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida: el nombre del documento; sin ella, el renglón va solo\n * @param {string|null} [props.value] lo que el servidor dio para reconocer el archivo subido ('' o null = ninguno)\n * @param {(value: string) => void} props.onChange recibe el id al terminar de subir, '' al quitarlo o mientras sube otro\n * @param {() => void} [props.onBlur] se llama al elegir o quitar (para que el formulario lo marque como tocado)\n * @param {(file: File, options: {signal: AbortSignal, idempotencyKey: string, onUploadProgress: (transfer: {loaded: number, total: number, rate?: number, estimated?: number}) => void}) => Promise<any>} props.upload\n * sube el archivo (con uploadWithProgress, axios o el cliente del proyecto) y regresa lo que conteste el servidor\n * @param {(result: any) => string} [props.idOf] de la respuesta, lo que se guarda en el formulario; por omisión result.id\n * @param {{name: string, size?: number}|null} [props.uploadedFile] el que ya estaba subido, para enseñar su nombre al editar\n * @param {(value: string, transferOptions: object) => Promise<{blob: Blob, fileName: string}>} [props.download] baja el\n * que ya subió (pasa por el panel de descargas); sin él no sale el ícono\n * @param {string|string[]} [props.accept] 'pdf' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string} [props.limitsText] ya traducido: qué acepta, en palabras («PDF o foto, hasta 5 MB»); sale al pasar sobre\n * el botón. Por omisión solo el peso máximo\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 {import('react').ReactNode} [props.actions] lo del negocio junto al estado (Aprobado, Falta información)\n * @param {(result: any) => void} [props.onUploaded] terminó de subir (p. ej. para enseñar el folio de la respuesta)\n * @param {(error: unknown) => void} [props.onUploadFailed] no se pudo subir (el motivo ya sale abajo)\n * @param {() => void} [props.onCancel] la persona canceló a media subida\n * @param {() => void} [props.onRemove] la persona quitó el archivo\n * @param {(status: string) => void} [props.onStatusChange] uno de FILE_UPLOAD_STATUS, cada vez que cambia\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 FileUploadInputContent(props) {\n const {\n name, label, value = '', onChange, onBlur, upload, idOf = (result) => result?.id, uploadedFile = null, download, accept, maxSize,\n limitsText, error, helperText = ' ', disabled = false, required = false, actions, onUploaded, onUploadFailed, onCancel, onRemove, onStatusChange,\n 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 inputId = `${name ?? 'file-upload'}-${generatedId}`;\n const labelId = `${inputId}-label`;\n const buttonTextId = `${inputId}-button`;\n const [chosenFile, setChosenFile] = useState(null);\n const [rejectedText, setRejectedText] = useState(null);\n const startedAtRef = useRef(0);\n const [uploadSnapshot, sendToUpload] = useMachine(uploadMachine.provide({\n actors: buildUploadActors(({file, idempotencyKey}, onProgress, signal) => upload(file, {\n signal,\n idempotencyKey,\n onUploadProgress: (transfer) => {\n const total = transfer.total ?? file.size;\n onProgress(total ? Math.round((transfer.loaded * 100) / total) : 0, {...transfer, total});\n },\n })),\n }));\n const {progress, transfer, result, problem} = uploadSnapshot.context;\n const isSending = uploadSnapshot.matches({uploading: 'sending'});\n const isProcessing = uploadSnapshot.matches({uploading: 'processing'});\n const isUploading = isSending || isProcessing;\n const isFailed = uploadSnapshot.matches('failed') || Boolean(rejectedText);\n const isUploaded = Boolean(value) && !isUploading && !isFailed;\n let status = FILE_UPLOAD_STATUS.pending;\n if (isSending) {\n status = FILE_UPLOAD_STATUS.uploading;\n } else if (isProcessing) {\n status = FILE_UPLOAD_STATUS.processing;\n } else if (isFailed) {\n status = FILE_UPLOAD_STATUS.failed;\n } else if (isUploaded) {\n status = FILE_UPLOAD_STATUS.uploaded;\n }\n\n // al terminar, el formulario se queda con el id; quien usa el campo puede enseñar lo demás de la respuesta\n useEffect(() => {\n if (!result) {\n return;\n }\n onChange(idOf(result) ?? '');\n onUploaded?.(result);\n // OJO: solo cuando llega una respuesta nueva; onChange, idOf y onUploaded cambian en cada render\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result]);\n useEffect(() => {\n if (problem) {\n onUploadFailed?.(problem);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [problem]);\n useEffect(() => {\n onStatusChange?.(status);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status]);\n // si el formulario se limpia (después de guardar), el renglón también regresa a «por cargar»\n useEffect(() => {\n if (!value && uploadSnapshot.matches('done')) {\n setChosenFile(null);\n sendToUpload({type: 'RESET'});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n const startUpload = (file) => {\n onBlur?.();\n setRejectedText(null);\n setChosenFile(file);\n // mientras sube, el formulario no tiene archivo: no se puede guardar con el id del anterior\n onChange('');\n startedAtRef.current = Date.now();\n sendToUpload({type: 'UPLOAD', input: {file, idempotencyKey: newIdentifier()}});\n };\n const picker = useFilePicker({\n accept, maxSize, disabled: disabled || isUploading, onAccept: startUpload, texts,\n onReject: (file, reason) => {\n onBlur?.();\n setRejectedText(reason === FILE_REJECTION.size\n ? t('upload.tooLarge', {name: file.name, max: formatFileSize(picker.maxBytes)})\n : t('upload.invalidType', {name: file.name, accept: picker.resolvedAccept}));\n },\n });\n const remove = () => {\n setChosenFile(null);\n setRejectedText(null);\n onChange('');\n onBlur?.();\n sendToUpload({type: 'RESET'});\n onRemove?.();\n };\n const cancel = () => {\n sendToUpload({type: 'CANCEL'});\n onCancel?.();\n };\n\n const shownFile = chosenFile ?? (value ? uploadedFile : null);\n const statusLabel = status === FILE_UPLOAD_STATUS.uploading\n ? t('upload.status.uploading', {percent: progress ?? 0})\n : t(`upload.status.${status}`);\n const validationText = validationTextOf(error);\n const errorText = rejectedText\n ?? (uploadSnapshot.matches('failed') ? uploadErrorTextOf(problem) ?? t('upload.failed') : null)\n ?? (isUploading ? null : validationText);\n let progressText = null;\n if (isProcessing) {\n progressText = t('upload.processing');\n } else if (isSending) {\n // «1.2 MB de 3 MB · 350 kB/s · faltan 5 segundos»: la velocidad y lo que falta, cuando ya se pueden decir (el\n // porcentaje ya va en la etiqueta del estado)\n const now = transfer ? transferProgressOf(transfer, startedAtRef.current) : null;\n progressText = now ? [\n t('upload.amount', {loaded: formatFileSize(now.loaded), total: formatFileSize(now.total)}),\n now.bytesPerSecond ? t('upload.speed', {size: formatFileSize(now.bytesPerSecond)}) : null,\n now.remainingSeconds != null ? t('upload.remaining', {count: now.remainingSeconds}) : null,\n ].filter(Boolean).join(PROGRESS_SEPARATOR) : t('upload.starting');\n } else if (uploadSnapshot.matches('cancelled')) {\n progressText = t('upload.cancelled');\n }\n const buttonHint = limitsText ?? (picker.maxBytes ? t('upload.maxSizeHint', {max: formatFileSize(picker.maxBytes)}) : '');\n const downloadUploaded = () => startDownload({\n label: shownFile?.name ?? t('upload.download'),\n request: (transferOptions) => download(value, transferOptions),\n });\n\n const row = (\n <div>\n <FileUploadRoot\n direction=\"row\"\n aria-busy={isUploading}\n {...picker.dropHandlers}\n ownSx={{\n // en el teléfono el nombre baja a su renglón; en escritorio se corta con «…» y el botón no se mueve\n alignItems: 'center', justifyContent: 'flex-end', flexWrap: {xs: 'wrap', sm: 'nowrap'}, gap: 1, minWidth: 0, minHeight: 40,\n px: 0.5, mx: -0.5, borderRadius: 1,\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} id={inputId} name={name} aria-labelledby={label ? labelId : undefined} />\n {shownFile && (\n // el nombre cortado si es largo y completo al pasar el cursor; en el teléfono baja a su renglón\n <Tooltip title={shownFile.size != null ? `${shownFile.name} · ${formatFileSize(shownFile.size)}` : shownFile.name}>\n <FileUploadName variant=\"caption\" color=\"text.secondary\" noWrap\n ownSx={{minWidth: 0, flexShrink: 1, flexBasis: {xs: '100%', sm: 'auto'}, textAlign: {xs: 'right', sm: 'left'}}}>\n {shownFile.name}\n </FileUploadName>\n </Tooltip>\n )}\n {actions}\n <FileUploadStatus\n size=\"small\"\n color={STATUS_COLORS[status]}\n variant={status === FILE_UPLOAD_STATUS.pending ? 'filled' : 'outlined'}\n label={statusLabel}\n icon={(isUploading && (\n <CircularProgress size={12} color=\"inherit\" variant={isProcessing ? 'indeterminate' : 'determinate'} value={progress ?? 0} />\n )) || (isUploaded && <CheckCircleRoundedIcon />) || undefined}\n ownSx={{flexShrink: 0}}\n />\n {isUploading ? (\n <FileUploadButton size=\"small\" color=\"inherit\" onClick={cancel} ownSx={{flexShrink: 0}}>{t('upload.cancel')}</FileUploadButton>\n ) : (\n // describeChild: lo que acepta se lee como descripción; el nombre sigue siendo «Cargar» y el documento\n <Tooltip title={buttonHint} describeChild>\n <FileUploadButton\n size=\"small\"\n variant={isUploaded ? 'outlined' : 'contained'}\n startIcon={<FileUploadOutlinedIcon />}\n disabled={disabled}\n onClick={picker.open}\n aria-labelledby={label ? `${buttonTextId} ${labelId}` : undefined}\n ownSx={{flexShrink: 0}}\n >\n <span id={buttonTextId}>{isUploaded ? t('upload.replace') : t('upload.upload')}</span>\n </FileUploadButton>\n </Tooltip>\n )}\n {isUploaded && download && (\n <Tooltip title={t('upload.download')}>\n <IconButton size=\"small\" aria-label={t('upload.download')} onClick={downloadUploaded}>\n <FileDownloadOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n {isUploaded && !disabled && (\n <Tooltip title={t('upload.remove', {name: shownFile?.name ?? ''})}>\n <IconButton size=\"small\" aria-label={t('upload.remove', {name: shownFile?.name ?? ''})} onClick={remove}>\n <CloseRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </FileUploadRoot>\n {/* abajo: el error manda; si no, el avance; si no, la ayuda de la pantalla */}\n <FileUploadHelper error={Boolean(errorText)} role={errorText ? 'alert' : (progressText ? 'status' : undefined)}\n ownSx={{textAlign: 'right', mx: 0}}>\n {errorText ?? progressText ?? helperText}\n </FileUploadHelper>\n </div>\n );\n\n // sin etiqueta, el renglón va solo (la pantalla pone el título del documento a su manera)\n if (!label) {\n return row;\n }\n return <FieldFrame inputId={inputId} labelId={labelId} label={label} required={required}>{row}</FieldFrame>;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FileUploadInput = withPieceSlots(FileUploadInputContent, THEME_NAME);\nexport default FileUploadInput;\n"],"names":["result","transfer"],"mappings":"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"}
1
+ {"version":3,"file":"FileUploadInput.js","sources":["../../src/upload/FileUploadInput.jsx"],"sourcesContent":["// El cargador MINI: un archivo que se SUBE en cuanto se elige, del tamaño de un renglón. Para una lista de documentos (el\n// expediente de un cliente, lo que pide una solicitud): a la izquierda el nombre del documento (la etiqueta del renglón) y a\n// la derecha el nombre del archivo, su estado y el botón.\n//\n// estado Por cargar · Subiendo 40 % · Procesando · Cargado · No se cargó\n// botón Cargar (lleno) → ya cargado, Sustituir (con borde); mientras sube, Cancelar\n// íconos bajar el que ya subió (si la pantalla da download) y quitarlo\n// abajo el avance en palabras («1.2 MB de 3 MB · 350 kB/s · faltan 5 segundos»; el porcentaje ya va en el estado) o el\n// motivo del error\n//\n// Al formulario llega SOLO lo que el servidor da para reconocer el archivo (su id, por omisión result.id): el formulario\n// manda ese id junto con lo demás y el backend lo liga a su registro. Mientras sube, el valor es '' (no se puede guardar\n// con el id del anterior). Subir es de la pantalla: `upload` recibe el archivo con su señal para cancelar, su avance y una\n// llave por archivo elegido (si la red se cae a media subida, el servidor no deja dos copias). Con uploadWithProgress o\n// con axios, igual.\n//\n// Lo que es del negocio (Aprobado, Falta información) no va aquí: la pantalla lo pinta junto, con `actions`, y sabe en\n// qué va cada uno con onStatusChange (para «3 de 5 cargados»). Tipo y peso se revisan ANTES de subir (accept, maxSize): no\n// se gastan minutos subiendo lo que el servidor va a rechazar; el motivo sale abajo, en rojo.\n//\n// Dentro de una FormSection se vuelve renglón, como los demás campos. También se puede soltar el archivo encima.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFileUploadInput con defaultProps y\n// styleOverrides de sus partes: root, fileName, status, button, helper.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useRef, useState} from 'react';\nimport {useMachine} from '@xstate/react';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\nimport FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';\nimport FileUploadOutlinedIcon from '@mui/icons-material/FileUploadOutlined';\n\nimport {startDownload} from '@/downloads/downloadQueue.js';\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport {newIdentifier} from '@/identifiers.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {fieldErrorsOf} from '@/problems.js';\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 {FILE_REJECTION} from '@/upload/fileRules.js';\nimport {transferProgressOf} from '@/upload/transferProgress.js';\nimport {buildUploadActors, uploadMachine} from '@/upload/uploadMachine.js';\nimport {useFilePicker} from '@/upload/useFilePicker.js';\n\n/** En qué va el archivo de un renglón (onStatusChange). */\nexport const FILE_UPLOAD_STATUS = Object.freeze({\n pending: 'pending', uploading: 'uploading', processing: 'processing', uploaded: 'uploaded', failed: 'failed',\n});\n\n// el color de la etiqueta de cada estado (el color es información)\nconst STATUS_COLORS = Object.freeze({\n [FILE_UPLOAD_STATUS.pending]: 'default',\n [FILE_UPLOAD_STATUS.uploading]: 'info',\n [FILE_UPLOAD_STATUS.processing]: 'info',\n [FILE_UPLOAD_STATUS.uploaded]: 'success',\n [FILE_UPLOAD_STATUS.failed]: 'error',\n});\n\n// entre las partes del avance en palabras\nconst PROGRESS_SEPARATOR = ' · ';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFileUploadInput)\nconst THEME_NAME = 'CalcitaFileUploadInput';\nconst FileUploadRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst FileUploadName = piecePart(THEME_NAME, 'FileName', Typography);\nconst FileUploadStatus = piecePart(THEME_NAME, 'Status', Chip);\nconst FileUploadButton = piecePart(THEME_NAME, 'Button', Button);\nconst FileUploadHelper = piecePart(THEME_NAME, 'Helper', FormHelperText);\n\n/**\n * El texto de un error de la subida: el del campo si el servidor mandó un 422 con su lista, si no su detail, y si no hubo\n * respuesta, el genérico.\n *\n * @param {unknown} error\n * @returns {string|null}\n */\nfunction uploadErrorTextOf(error) {\n const problem = /** @type {{problem?: object, message?: string}} */ (error)?.problem;\n const firstFieldMessage = Object.values(fieldErrorsOf(problem ?? null)).flat()[0];\n return firstFieldMessage ?? problem?.detail ?? null;\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida: el nombre del documento; sin ella, el renglón va solo\n * @param {string|null} [props.value] lo que el servidor dio para reconocer el archivo subido ('' o null = ninguno)\n * @param {(value: string) => void} props.onChange recibe el id al terminar de subir, '' al quitarlo o mientras sube otro\n * @param {() => void} [props.onBlur] se llama al elegir o quitar (para que el formulario lo marque como tocado)\n * @param {(file: File, options: {signal: AbortSignal, idempotencyKey: string, onUploadProgress: (transfer: {loaded: number, total: number, rate?: number, estimated?: number}) => void}) => Promise<any>} props.upload\n * sube el archivo (con uploadWithProgress, axios o el cliente del proyecto) y regresa lo que conteste el servidor\n * @param {(result: any) => string} [props.idOf] de la respuesta, lo que se guarda en el formulario; por omisión result.id\n * @param {{name: string, size?: number}|null} [props.uploadedFile] el que ya estaba subido, para enseñar su nombre al editar\n * @param {(value: string, transferOptions: object) => Promise<{blob: Blob, fileName: string}>} [props.download] baja el\n * que ya subió (pasa por el panel de descargas); sin él no sale el ícono\n * @param {string|string[]} [props.accept] 'pdf' | ['pdf', 'image'] | '.xlsx,.xls' | 'image/*'\n * @param {number|string} [props.maxSize] bytes o '10 MB'\n * @param {string} [props.limitsText] ya traducido: qué acepta, en palabras («PDF o foto, hasta 5 MB»); sale al pasar sobre\n * el botón. Por omisión solo el peso máximo\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 {import('react').ReactNode} [props.actions] lo del negocio junto al estado (Aprobado, Falta información)\n * @param {(result: any) => void} [props.onUploaded] terminó de subir (p. ej. para enseñar el folio de la respuesta)\n * @param {(error: unknown) => void} [props.onUploadFailed] no se pudo subir (el motivo ya sale abajo)\n * @param {() => void} [props.onCancel] la persona canceló a media subida\n * @param {() => void} [props.onRemove] la persona quitó el archivo\n * @param {(status: string) => void} [props.onStatusChange] uno de FILE_UPLOAD_STATUS, cada vez que cambia\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 FileUploadInputContent(props) {\n const {\n name, label, value = '', onChange, onBlur, upload, idOf = (result) => result?.id, uploadedFile = null, download, accept, maxSize,\n limitsText, error, helperText = ' ', disabled = false, required = false, actions, onUploaded, onUploadFailed, onCancel, onRemove, onStatusChange,\n 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 inputId = `${name ?? 'file-upload'}-${generatedId}`;\n const labelId = `${inputId}-label`;\n const buttonTextId = `${inputId}-button`;\n const [chosenFile, setChosenFile] = useState(null);\n const [rejectedText, setRejectedText] = useState(null);\n const startedAtRef = useRef(0);\n const [uploadSnapshot, sendToUpload] = useMachine(uploadMachine.provide({\n actors: buildUploadActors(({file, idempotencyKey}, onProgress, signal) => upload(file, {\n signal,\n idempotencyKey,\n onUploadProgress: (transfer) => {\n const total = transfer.total ?? file.size;\n onProgress(total ? Math.round((transfer.loaded * 100) / total) : 0, {...transfer, total});\n },\n })),\n }));\n const {progress, transfer, result, problem} = uploadSnapshot.context;\n const isSending = uploadSnapshot.matches({uploading: 'sending'});\n const isProcessing = uploadSnapshot.matches({uploading: 'processing'});\n const isUploading = isSending || isProcessing;\n const isFailed = uploadSnapshot.matches('failed') || Boolean(rejectedText);\n const isUploaded = Boolean(value) && !isUploading && !isFailed;\n let status = FILE_UPLOAD_STATUS.pending;\n if (isSending) {\n status = FILE_UPLOAD_STATUS.uploading;\n } else if (isProcessing) {\n status = FILE_UPLOAD_STATUS.processing;\n } else if (isFailed) {\n status = FILE_UPLOAD_STATUS.failed;\n } else if (isUploaded) {\n status = FILE_UPLOAD_STATUS.uploaded;\n }\n\n // al terminar, el formulario se queda con el id; quien usa el campo puede enseñar lo demás de la respuesta\n useEffect(() => {\n if (!result) {\n return;\n }\n onChange(idOf(result) ?? '');\n onUploaded?.(result);\n // OJO: solo cuando llega una respuesta nueva; onChange, idOf y onUploaded cambian en cada render\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result]);\n useEffect(() => {\n if (problem) {\n onUploadFailed?.(problem);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [problem]);\n useEffect(() => {\n onStatusChange?.(status);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [status]);\n // si el formulario se limpia (después de guardar), el renglón también regresa a «por cargar»\n useEffect(() => {\n if (!value && uploadSnapshot.matches('done')) {\n setChosenFile(null);\n sendToUpload({type: 'RESET'});\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n const startUpload = (file) => {\n onBlur?.();\n setRejectedText(null);\n setChosenFile(file);\n // mientras sube, el formulario no tiene archivo: no se puede guardar con el id del anterior\n onChange('');\n startedAtRef.current = Date.now();\n sendToUpload({type: 'UPLOAD', input: {file, idempotencyKey: newIdentifier()}});\n };\n const picker = useFilePicker({\n accept, maxSize, disabled: disabled || isUploading, onAccept: startUpload, texts,\n onReject: (file, reason) => {\n onBlur?.();\n setRejectedText(reason === FILE_REJECTION.size\n ? t('upload.tooLarge', {name: file.name, max: formatFileSize(picker.maxBytes)})\n : t('upload.invalidType', {name: file.name, accept: picker.resolvedAccept}));\n },\n });\n const remove = () => {\n setChosenFile(null);\n setRejectedText(null);\n onChange('');\n onBlur?.();\n sendToUpload({type: 'RESET'});\n onRemove?.();\n };\n const cancel = () => {\n sendToUpload({type: 'CANCEL'});\n onCancel?.();\n };\n\n const shownFile = chosenFile ?? (value ? uploadedFile : null);\n const statusLabel = status === FILE_UPLOAD_STATUS.uploading\n ? t('upload.status.uploading', {percent: progress ?? 0})\n : t(`upload.status.${status}`);\n const validationText = validationTextOf(error);\n const errorText = rejectedText\n ?? (uploadSnapshot.matches('failed') ? uploadErrorTextOf(problem) ?? t('upload.failed') : null)\n ?? (isUploading ? null : validationText);\n let progressText = null;\n if (isProcessing) {\n progressText = t('upload.processing');\n } else if (isSending) {\n // «1.2 MB de 3 MB · 350 kB/s · faltan 5 segundos»: la velocidad y lo que falta, cuando ya se pueden decir (el\n // porcentaje ya va en la etiqueta del estado)\n const now = transfer ? transferProgressOf(transfer, startedAtRef.current) : null;\n progressText = now ? [\n t('upload.amount', {loaded: formatFileSize(now.loaded), total: formatFileSize(now.total)}),\n now.bytesPerSecond ? t('upload.speed', {size: formatFileSize(now.bytesPerSecond)}) : null,\n now.remainingSeconds != null ? t('upload.remaining', {count: now.remainingSeconds}) : null,\n ].filter(Boolean).join(PROGRESS_SEPARATOR) : t('upload.starting');\n } else if (uploadSnapshot.matches('cancelled')) {\n progressText = t('upload.cancelled');\n }\n const buttonHint = limitsText ?? (picker.maxBytes ? t('upload.maxSizeHint', {max: formatFileSize(picker.maxBytes)}) : '');\n const downloadUploaded = () => startDownload({\n label: shownFile?.name ?? t('upload.download'),\n request: (transferOptions) => download(value, transferOptions),\n });\n\n const row = (\n <div>\n <FileUploadRoot\n direction=\"row\"\n aria-busy={isUploading}\n {...picker.dropHandlers}\n ownSx={{\n // en el teléfono el nombre baja a su renglón; en escritorio se corta con «…» y el botón no se mueve\n alignItems: 'center', justifyContent: 'flex-end', flexWrap: {xs: 'wrap', sm: 'nowrap'}, gap: 1, minWidth: 0, minHeight: 40,\n px: 0.5, mx: -0.5, borderRadius: 1,\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} id={inputId} name={name} aria-labelledby={label ? labelId : undefined} />\n {shownFile && (\n // el nombre cortado si es largo y completo al pasar el cursor; en el teléfono baja a su renglón\n <Tooltip title={shownFile.size != null ? `${shownFile.name} · ${formatFileSize(shownFile.size)}` : shownFile.name}>\n <FileUploadName variant=\"caption\" color=\"text.secondary\" noWrap\n ownSx={{minWidth: 0, flexShrink: 1, flexBasis: {xs: '100%', sm: 'auto'}, textAlign: {xs: 'right', sm: 'left'}}}>\n {shownFile.name}\n </FileUploadName>\n </Tooltip>\n )}\n {actions}\n <FileUploadStatus\n size=\"small\"\n color={STATUS_COLORS[status]}\n variant={status === FILE_UPLOAD_STATUS.pending ? 'filled' : 'outlined'}\n label={statusLabel}\n icon={(isUploading && (\n <CircularProgress size={12} color=\"inherit\" variant={isProcessing ? 'indeterminate' : 'determinate'} value={progress ?? 0} />\n )) || (isUploaded && <CheckCircleRoundedIcon />) || undefined}\n ownSx={{flexShrink: 0}}\n />\n {isUploading ? (\n <FileUploadButton size=\"small\" color=\"inherit\" onClick={cancel} ownSx={{flexShrink: 0}}>{t('upload.cancel')}</FileUploadButton>\n ) : (\n // describeChild: lo que acepta se lee como descripción; el nombre sigue siendo «Cargar» y el documento\n <Tooltip title={buttonHint} describeChild>\n <FileUploadButton\n size=\"small\"\n variant={isUploaded ? 'outlined' : 'contained'}\n startIcon={<FileUploadOutlinedIcon />}\n disabled={disabled}\n onClick={picker.open}\n aria-labelledby={label ? `${buttonTextId} ${labelId}` : undefined}\n ownSx={{flexShrink: 0}}\n >\n <span id={buttonTextId}>{isUploaded ? t('upload.replace') : t('upload.upload')}</span>\n </FileUploadButton>\n </Tooltip>\n )}\n {isUploaded && download && (\n <Tooltip title={t('upload.download')}>\n <IconButton size=\"small\" aria-label={t('upload.download')} onClick={downloadUploaded}>\n <FileDownloadOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n {isUploaded && !disabled && (\n <Tooltip title={t('upload.remove', {name: shownFile?.name ?? ''})}>\n <IconButton size=\"small\" aria-label={t('upload.remove', {name: shownFile?.name ?? ''})} onClick={remove}>\n <CloseRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </FileUploadRoot>\n {/* abajo: el error manda; si no, el avance; si no, la ayuda de la pantalla. width 0 con minWidth 100 %: el texto\n usa el ancho de los botones de arriba y nunca lo ensancha. OJO: en un renglón que mide lo que ocupa (FormSection\n con labelWidth auto) el avance, que cambia cada momento, empujaba el nombre del documento a cada rato */}\n <FileUploadHelper error={Boolean(errorText)} role={errorText ? 'alert' : (progressText ? 'status' : undefined)}\n ownSx={{textAlign: 'right', mx: 0, width: 0, minWidth: '100%'}}>\n {errorText ?? progressText ?? helperText}\n </FileUploadHelper>\n </div>\n );\n\n // sin etiqueta, el renglón va solo (la pantalla pone el título del documento a su manera)\n if (!label) {\n return row;\n }\n return <FieldFrame inputId={inputId} labelId={labelId} label={label} required={required}>{row}</FieldFrame>;\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FileUploadInput = withPieceSlots(FileUploadInputContent, THEME_NAME);\nexport default FileUploadInput;\n"],"names":["result","transfer"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackcraftmx/calcita",
3
- "version": "0.54.0",
3
+ "version": "0.56.0",
4
4
  "description": "Componentes y utilidades de React con MUI para arrancar proyectos rápido: tabla de servidor, diálogos, avisos, descargas, formularios, formatos y reportes en Excel.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "author": "calcifux",