@stackcraftmx/calcita 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -6
- package/cli/calcita.js +4 -2
- package/cli/checkTexts.js +66 -9
- package/dist/appShell/BarActionButton.js.map +1 -1
- package/dist/datePickers/DateInput.js +5 -3
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +6 -4
- package/dist/datePickers/DateTimeInput.js.map +1 -1
- package/dist/datePickers/FormDateField.js +1 -1
- package/dist/datePickers/FormDateField.js.map +1 -1
- package/dist/datePickers/FormDateTimeField.js +1 -1
- package/dist/datePickers/FormDateTimeField.js.map +1 -1
- package/dist/datePickers/FormMonthField.js +1 -1
- package/dist/datePickers/FormMonthField.js.map +1 -1
- package/dist/datePickers/FormTimeField.js +1 -1
- package/dist/datePickers/FormTimeField.js.map +1 -1
- package/dist/datePickers/MonthInput.js +5 -3
- package/dist/datePickers/MonthInput.js.map +1 -1
- package/dist/datePickers/PickerFrame.js +2 -2
- package/dist/datePickers/PickerFrame.js.map +1 -1
- package/dist/datePickers/TimeInput.js +6 -4
- package/dist/datePickers/TimeInput.js.map +1 -1
- package/dist/feedback/Toaster.js +11 -2
- package/dist/feedback/Toaster.js.map +1 -1
- package/dist/forms/CalcitaFormik.js +11 -0
- package/dist/forms/CalcitaFormik.js.map +1 -0
- package/dist/forms/FormAttachmentsField.js +1 -1
- package/dist/forms/FormAttachmentsField.js.map +1 -1
- package/dist/forms/FormCatalogField.js +1 -1
- package/dist/forms/FormCatalogField.js.map +1 -1
- package/dist/forms/FormCheckboxGroupField.js +1 -1
- package/dist/forms/FormCheckboxGroupField.js.map +1 -1
- package/dist/forms/FormColorField.js +1 -1
- package/dist/forms/FormColorField.js.map +1 -1
- package/dist/forms/FormFileField.js +1 -1
- package/dist/forms/FormFileField.js.map +1 -1
- package/dist/forms/FormFileUploadField.js +1 -1
- package/dist/forms/FormFileUploadField.js.map +1 -1
- package/dist/forms/FormImageField.js +1 -1
- package/dist/forms/FormImageField.js.map +1 -1
- package/dist/forms/FormMaskedField.js +1 -1
- package/dist/forms/FormMaskedField.js.map +1 -1
- package/dist/forms/FormNumberField.js +1 -1
- package/dist/forms/FormNumberField.js.map +1 -1
- package/dist/forms/FormSchemaProvider.js +17 -0
- package/dist/forms/FormSchemaProvider.js.map +1 -0
- package/dist/forms/FormSelectField.js +1 -1
- package/dist/forms/FormSelectField.js.map +1 -1
- package/dist/forms/FormTextField.js +1 -1
- package/dist/forms/FormTextField.js.map +1 -1
- package/dist/forms/FormTransferListField.js +1 -1
- package/dist/forms/FormTransferListField.js.map +1 -1
- package/dist/forms/FormWizard.js +23 -6
- package/dist/forms/FormWizard.js.map +1 -1
- package/dist/forms/schemaRequired.js +33 -0
- package/dist/forms/schemaRequired.js.map +1 -0
- package/dist/forms/useFormikField.js +18 -5
- package/dist/forms/useFormikField.js.map +1 -1
- package/dist/forms.js +52 -46
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormAttachmentsField.js +1 -1
- package/dist/hookForm/FormAttachmentsField.js.map +1 -1
- package/dist/hookForm/FormCatalogField.js +1 -1
- package/dist/hookForm/FormCatalogField.js.map +1 -1
- package/dist/hookForm/FormCheckboxGroupField.js +1 -1
- package/dist/hookForm/FormCheckboxGroupField.js.map +1 -1
- package/dist/hookForm/FormColorField.js +1 -1
- package/dist/hookForm/FormColorField.js.map +1 -1
- package/dist/hookForm/FormFileField.js +1 -1
- package/dist/hookForm/FormFileField.js.map +1 -1
- package/dist/hookForm/FormFileUploadField.js +1 -1
- package/dist/hookForm/FormFileUploadField.js.map +1 -1
- package/dist/hookForm/FormImageField.js +1 -1
- package/dist/hookForm/FormImageField.js.map +1 -1
- package/dist/hookForm/FormMaskedField.js +1 -1
- package/dist/hookForm/FormMaskedField.js.map +1 -1
- package/dist/hookForm/FormNumberField.js +1 -1
- package/dist/hookForm/FormNumberField.js.map +1 -1
- package/dist/hookForm/FormSelectField.js +1 -1
- package/dist/hookForm/FormSelectField.js.map +1 -1
- package/dist/hookForm/FormTextField.js +1 -1
- package/dist/hookForm/FormTextField.js.map +1 -1
- package/dist/hookForm/FormTransferListField.js +1 -1
- package/dist/hookForm/FormTransferListField.js.map +1 -1
- package/dist/hookForm/FormWizard.js +21 -5
- package/dist/hookForm/FormWizard.js.map +1 -1
- package/dist/hookForm/useHookFormField.js +9 -3
- package/dist/hookForm/useHookFormField.js.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +3 -2
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +2 -1
- package/dist/inputs/CheckboxGroupInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +3 -1
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FieldFrame.js +18 -3
- package/dist/inputs/FieldFrame.js.map +1 -1
- package/dist/inputs/FormSection.js +42 -11
- package/dist/inputs/FormSection.js.map +1 -1
- package/dist/inputs/NumberInput.js +3 -1
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/SelectInput.js +3 -1
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +1 -1
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/TransferListInput.js +2 -1
- package/dist/inputs/TransferListInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +51 -17
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/inputs/formLayoutContext.js +7 -2
- package/dist/inputs/formLayoutContext.js.map +1 -1
- package/dist/inputs/requiredMark.js +17 -0
- package/dist/inputs/requiredMark.js.map +1 -0
- package/dist/inputs/useWizardSteps.js +36 -0
- package/dist/inputs/useWizardSteps.js.map +1 -0
- package/dist/inputs/wizardSteps.js +32 -0
- package/dist/inputs/wizardSteps.js.map +1 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/richText/FormRichTextField.js +1 -1
- package/dist/richText/FormRichTextField.js.map +1 -1
- package/dist/richText/RichTextEditor.js +4 -3
- package/dist/richText/RichTextEditor.js.map +1 -1
- package/dist/toolkit/ToolkitProvider.js +3 -0
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/toolkitContext.js.map +1 -1
- package/dist/types/appShell/BarActionButton.d.ts +1 -0
- package/dist/types/datePickers/DateInput.d.ts +2 -0
- package/dist/types/datePickers/DateTimeInput.d.ts +2 -0
- package/dist/types/datePickers/FormDateField.d.ts +3 -3
- package/dist/types/datePickers/FormDateTimeField.d.ts +3 -3
- package/dist/types/datePickers/FormMonthField.d.ts +3 -3
- package/dist/types/datePickers/FormTimeField.d.ts +3 -3
- package/dist/types/datePickers/MonthInput.d.ts +2 -0
- package/dist/types/datePickers/PickerFrame.d.ts +3 -1
- package/dist/types/datePickers/TimeInput.d.ts +2 -0
- package/dist/types/feedback/Toaster.d.ts +15 -1
- package/dist/types/feedback/index.d.ts +1 -1
- package/dist/types/forms/CalcitaFormik.d.ts +6 -0
- package/dist/types/forms/FormAttachmentsField.d.ts +3 -3
- package/dist/types/forms/FormCatalogField.d.ts +2 -2
- package/dist/types/forms/FormCheckboxGroupField.d.ts +3 -3
- package/dist/types/forms/FormColorField.d.ts +2 -2
- package/dist/types/forms/FormFileField.d.ts +3 -3
- package/dist/types/forms/FormFileUploadField.d.ts +3 -3
- package/dist/types/forms/FormImageField.d.ts +3 -3
- package/dist/types/forms/FormMaskedField.d.ts +2 -2
- package/dist/types/forms/FormNumberField.d.ts +2 -2
- package/dist/types/forms/FormSchemaProvider.d.ts +13 -0
- package/dist/types/forms/FormSelectField.d.ts +2 -2
- package/dist/types/forms/FormTextField.d.ts +2 -2
- package/dist/types/forms/FormTransferListField.d.ts +3 -3
- package/dist/types/forms/FormWizard.d.ts +14 -1
- package/dist/types/forms/index.d.ts +1 -0
- package/dist/types/forms/schemaRequired.d.ts +14 -0
- package/dist/types/forms/useFormikField.d.ts +10 -4
- package/dist/types/forms.d.ts +4 -2
- package/dist/types/hookForm/FormAttachmentsField.d.ts +3 -3
- package/dist/types/hookForm/FormCatalogField.d.ts +3 -3
- package/dist/types/hookForm/FormCheckboxGroupField.d.ts +3 -3
- package/dist/types/hookForm/FormColorField.d.ts +3 -3
- package/dist/types/hookForm/FormFileField.d.ts +3 -3
- package/dist/types/hookForm/FormFileUploadField.d.ts +3 -3
- package/dist/types/hookForm/FormImageField.d.ts +3 -3
- package/dist/types/hookForm/FormMaskedField.d.ts +3 -3
- package/dist/types/hookForm/FormNumberField.d.ts +3 -3
- package/dist/types/hookForm/FormSelectField.d.ts +3 -3
- package/dist/types/hookForm/FormTextField.d.ts +3 -3
- package/dist/types/hookForm/FormTransferListField.d.ts +3 -3
- package/dist/types/hookForm/FormWizard.d.ts +14 -1
- package/dist/types/hookForm/useHookFormField.d.ts +8 -4
- package/dist/types/index.d.ts +4 -3
- package/dist/types/inputs/CatalogInput.d.ts +1 -0
- package/dist/types/inputs/CheckboxGroupInput.d.ts +1 -0
- package/dist/types/inputs/ColorInput.d.ts +1 -0
- package/dist/types/inputs/FieldFrame.d.ts +1 -0
- package/dist/types/inputs/FormSection.d.ts +13 -0
- package/dist/types/inputs/NumberInput.d.ts +1 -0
- package/dist/types/inputs/SelectInput.d.ts +1 -0
- package/dist/types/inputs/TextInput.d.ts +1 -1
- package/dist/types/inputs/TransferListInput.d.ts +1 -0
- package/dist/types/inputs/WizardLayout.d.ts +6 -2
- package/dist/types/inputs/formLayoutContext.d.ts +8 -1
- package/dist/types/inputs/requiredMark.d.ts +9 -0
- package/dist/types/inputs/useWizardSteps.d.ts +23 -0
- package/dist/types/inputs/wizardSteps.d.ts +65 -0
- package/dist/types/locales/en.d.ts +1 -0
- package/dist/types/locales/es.d.ts +1 -0
- package/dist/types/richText/FormRichTextField.d.ts +3 -3
- package/dist/types/richText/RichTextEditor.d.ts +1 -0
- package/dist/types/toolkit/ToolkitProvider.d.ts +5 -1
- package/dist/types/toolkit/toolkitContext.d.ts +2 -1
- package/dist/types/ui/cards/RecordCard.d.ts +12 -0
- package/dist/types/ui/index.d.ts +1 -1
- package/dist/types/ui/navigation/BackButton.d.ts +1 -0
- package/dist/types/upload/AttachmentsInput.d.ts +2 -0
- package/dist/types/upload/FileInput.d.ts +1 -0
- package/dist/types/upload/FileUploadInput.d.ts +1 -0
- package/dist/types/upload/ImageInput.d.ts +1 -0
- package/dist/ui/cards/RecordCard.js +6 -2
- package/dist/ui/cards/RecordCard.js.map +1 -1
- package/dist/ui/navigation/BackButton.js.map +1 -1
- package/dist/upload/AttachmentsInput.js +2 -1
- package/dist/upload/AttachmentsInput.js.map +1 -1
- package/dist/upload/FileInput.js +2 -1
- package/dist/upload/FileInput.js.map +1 -1
- package/dist/upload/FileUploadInput.js +2 -1
- package/dist/upload/FileUploadInput.js.map +1 -1
- package/dist/upload/ImageInput.js +2 -1
- package/dist/upload/ImageInput.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WizardLayout.js","sources":["../../src/inputs/WizardLayout.jsx"],"sourcesContent":["// Lo que se ve de un formulario por pasos: los pasos arriba (en el teléfono, «Paso 2 de 4» y su nombre), los campos del paso\n// y los botones (Anterior · Omitir este paso · Siguiente o Enviar). No sabe de formik ni de react-hook-form: el FormWizard de\n// cada uno (/forms y /react-hook-form) pone la lógica (qué revisa «Siguiente», qué hace «Omitir») y le pasa su botón de\n// enviar y su aviso de errores. Suelto sirve para armar un asistente propio con otra librería.\n//\n// Los textos se cambian con `texts`, con la forma de `common` en los textos del paquete: {wizard: {next: 'Continuar'}}.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWizardLayout con defaultProps y\n// styleOverrides de sus partes: stepper, phoneSteps, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Step from '@mui/material/Step';\nimport StepLabel from '@mui/material/StepLabel';\nimport Stepper from '@mui/material/Stepper';\nimport Typography from '@mui/material/Typography';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaWizardLayout)\nconst THEME_NAME = 'CalcitaWizardLayout';\nconst WizardStepper = piecePart(THEME_NAME, 'Stepper', Stepper);\nconst WizardPhoneSteps = piecePart(THEME_NAME, 'PhoneSteps', Box);\nconst WizardActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {number} props.activeIndex el paso que se ve\n * @param {Set<string>} props.skippedKeys los pasos omitidos\n * @param {boolean} [props.isSubmitting] mientras se envía, «Anterior» se apaga\n * @param {() => void} props.onBack\n * @param {() => void} props.onNext\n * @param {() => void} props.onSkip\n * @param {import('react').ReactNode} props.submitButton el botón de enviar del último paso\n * @param {import('react').ReactNode} [props.problemAlert] el aviso de errores del servidor, arriba de los botones del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nfunction WizardLayoutContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWizardLayout.defaultProps) y encima lo que llega aquí\n const {\n steps, activeIndex, skippedKeys, isSubmitting = false, onBack, onNext, onSkip, submitButton, problemAlert, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const activeStep = steps[activeIndex];\n const isLastStep = activeIndex === steps.length - 1;\n\n return (\n <Stack spacing={3}>\n {/* en el teléfono los nombres de los pasos no caben: se dice en qué paso va */}\n <WizardStepper activeStep={activeIndex} alternativeLabel ownSx={{display: {xs: 'none', sm: 'flex'}}}>\n {steps.map((step) => {\n let caption;\n if (skippedKeys.has(step.key)) {\n caption = t('wizard.skipped');\n } else if (step.optional) {\n caption = t('wizard.optional');\n }\n return (\n <Step key={step.key} completed={skippedKeys.has(step.key) ? false : undefined}>\n <StepLabel optional={caption && <Typography variant=\"caption\" color=\"text.secondary\">{caption}</Typography>}>\n {step.label}\n </StepLabel>\n </Step>\n );\n })}\n </WizardStepper>\n <WizardPhoneSteps ownSx={{display: {xs: 'block', sm: 'none'}}}>\n <Typography variant=\"overline\" color=\"text.secondary\">{t('wizard.stepOf', {current: activeIndex + 1, total: steps.length})}</Typography>\n <Typography variant=\"h6\">{activeStep.label}</Typography>\n </WizardPhoneSteps>\n\n <Box>{activeStep.content}</Box>\n\n {isLastStep && problemAlert}\n\n <WizardActions direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5} ownSx={{justifyContent: 'space-between'}}>\n <Button variant=\"text\" color=\"inherit\" disabled={activeIndex === 0 || isSubmitting} onClick={onBack}>\n {t('wizard.back')}\n </Button>\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5}>\n {activeStep.optional && !isLastStep && (\n <Button variant=\"text\" onClick={onSkip}>{t('wizard.skip')}</Button>\n )}\n {isLastStep\n ? submitButton\n : <Button variant=\"contained\" size=\"large\" onClick={onNext}>{t('wizard.next')}</Button>}\n </Stack>\n </WizardActions>\n </Stack>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst WizardLayout = withPieceSlots(WizardLayoutContent, THEME_NAME);\nexport default WizardLayout;\n"],"names":[],"mappings":";;;;;;;;;;;;AAuBA,MAAM,aAAa;AACnB,MAAM,gBAAgB,UAAU,YAAY,WAAW,OAAO;AAC9D,MAAM,mBAAmB,UAAU,YAAY,cAAc,GAAG;AAChE,MAAM,gBAAgB,UAAU,YAAY,WAAW,KAAK;AAmB5D,SAAS,oBAAoB,OAAO;AAEhC,QAAM;AAAA,IACN;AAAA,IAAO;AAAA,IAAa;AAAA,IAAa,eAAe;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAc;AAAA,IAAc;AAAA,IAC3G,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AAC/B,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,aAAa,MAAM,WAAW;AACpC,QAAM,aAAa,gBAAgB,MAAM,SAAS;AAElD,SACI,qBAAC,OAAA,EAAM,SAAS,GAEZ,UAAA;AAAA,IAAA,oBAAC,iBAAc,YAAY,aAAa,kBAAgB,MAAC,OAAO,EAAC,SAAS,EAAC,IAAI,QAAQ,IAAI,OAAA,EAAM,GAC5F,UAAA,MAAM,IAAI,CAAC,SAAS;AACjB,UAAI;AACJ,UAAI,YAAY,IAAI,KAAK,GAAG,GAAG;AAC3B,kBAAU,EAAE,gBAAgB;AAAA,MAChC,WAAW,KAAK,UAAU;AACtB,kBAAU,EAAE,iBAAiB;AAAA,MACjC;AACA,aACI,oBAAC,MAAA,EAAoB,WAAW,YAAY,IAAI,KAAK,GAAG,IAAI,QAAQ,QAChE,UAAA,oBAAC,WAAA,EAAU,UAAU,WAAW,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAkB,UAAA,QAAA,CAAQ,GACzF,UAAA,KAAK,MAAA,CACV,EAAA,GAHO,KAAK,GAIhB;AAAA,IAER,CAAC,EAAA,CACL;AAAA,IACA,qBAAC,kBAAA,EAAiB,OAAO,EAAC,SAAS,EAAC,IAAI,SAAS,IAAI,OAAA,EAAM,GACvD,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,YAAW,OAAM,kBAAkB,UAAA,EAAE,iBAAiB,EAAC,SAAS,cAAc,GAAG,OAAO,MAAM,OAAA,CAAO,GAAE;AAAA,MAC3H,oBAAC,YAAA,EAAW,SAAQ,MAAM,qBAAW,MAAA,CAAM;AAAA,IAAA,GAC/C;AAAA,IAEA,oBAAC,KAAA,EAAK,UAAA,WAAW,QAAA,CAAQ;AAAA,IAExB,cAAc;AAAA,IAEf,qBAAC,eAAA,EAAc,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KAAK,OAAO,EAAC,gBAAgB,mBAC/F,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAO,SAAQ,QAAO,OAAM,WAAU,UAAU,gBAAgB,KAAK,cAAc,SAAS,QACxF,UAAA,EAAE,aAAa,GACpB;AAAA,MACA,qBAAC,OAAA,EAAM,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KACzD,UAAA;AAAA,QAAA,WAAW,YAAY,CAAC,cACrB,oBAAC,QAAA,EAAO,SAAQ,QAAO,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,QAE7D,aACK,eACA,oBAAC,QAAA,EAAO,SAAQ,aAAY,MAAK,SAAQ,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,MAAA,EAAA,CACtF;AAAA,IAAA,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;AAGA,MAAM,eAAe,eAAe,qBAAqB,UAAU;"}
|
|
1
|
+
{"version":3,"file":"WizardLayout.js","sources":["../../src/inputs/WizardLayout.jsx"],"sourcesContent":["// Lo que se ve de un formulario por pasos: los pasos, los campos del paso abierto y los botones (Anterior · Omitir este paso ·\n// Siguiente o Enviar). No sabe de formik ni de react-hook-form: el FormWizard de cada uno (/forms y /react-hook-form) pone la\n// lógica (qué revisa «Siguiente», qué hace «Omitir», si se puede saltar a otro paso) y le pasa su botón de enviar y su aviso de\n// errores. Suelto sirve para armar un asistente propio con otra librería.\n//\n// Dos acomodos (WIZARD_ORIENTATION):\n// horizontal los pasos en fila arriba y los campos abajo; en el teléfono no caben los nombres: «Paso 2 de 4» y su nombre;\n// vertical uno debajo del otro, como un acordeón: los campos y los botones van dentro del paso abierto (también en el\n// teléfono). Es el de editar un expediente: se ve de un vistazo qué pasos hay y se entra al que hace falta.\n//\n// Con onStepSelect los títulos de los pasos se pulsan (con teclado también: son botones) y avisan qué paso se pidió; quien lo\n// usa decide si salta. Sin él, los títulos solo informan (lo de siempre). Omitir es opcional: sin onSkip no sale el botón.\n//\n// Los textos se cambian con `texts`, con la forma de `common` en los textos del paquete: {wizard: {next: 'Continuar'}}.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWizardLayout con defaultProps y\n// styleOverrides de sus partes: stepper, phoneSteps, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Step from '@mui/material/Step';\nimport StepButton from '@mui/material/StepButton';\nimport StepContent from '@mui/material/StepContent';\nimport StepLabel from '@mui/material/StepLabel';\nimport Stepper from '@mui/material/Stepper';\nimport Typography from '@mui/material/Typography';\nimport {WIZARD_ORIENTATION, isStepCompleted} from '@/inputs/wizardSteps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// sin pasos omitidos (quien no usa «Omitir» no tiene que mandar nada)\nconst NO_SKIPPED_KEYS = new Set();\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaWizardLayout)\nconst THEME_NAME = 'CalcitaWizardLayout';\nconst WizardStepper = piecePart(THEME_NAME, 'Stepper', Stepper);\nconst WizardPhoneSteps = piecePart(THEME_NAME, 'PhoneSteps', Box);\nconst WizardActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {boolean} [props.steps[].completed] lleva la palomita; por omisión, en fila y sin pulsar, los de antes del abierto\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {number} props.activeIndex el paso que se ve\n * @param {Set<string>} [props.skippedKeys] los pasos omitidos\n * @param {boolean} [props.isSubmitting] mientras se envía, «Anterior» se apaga\n * @param {'horizontal'|'vertical'} [props.orientation] uno de WIZARD_ORIENTATION; por omisión horizontal\n * @param {boolean} [props.hideBackOnFirstStep] en el primer paso no se pinta «Anterior» (no hay a dónde volver); por omisión\n * sale apagado, como antes\n * @param {() => void} props.onBack\n * @param {() => void} props.onNext\n * @param {() => void} [props.onSkip] sin él no sale «Omitir este paso»\n * @param {(index: number) => void} [props.onStepSelect] con él, los títulos de los pasos se pulsan y avisan cuál se pidió\n * @param {import('react').ReactNode} props.submitButton el botón de enviar del último paso\n * @param {import('react').ReactNode} [props.problemAlert] el aviso de errores del servidor, arriba de los botones del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nfunction WizardLayoutContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWizardLayout.defaultProps) y encima lo que llega aquí\n const {\n steps, activeIndex, skippedKeys = NO_SKIPPED_KEYS, isSubmitting = false, orientation = WIZARD_ORIENTATION.horizontal,\n hideBackOnFirstStep = false, onBack, onNext, onSkip, onStepSelect, submitButton, problemAlert, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const activeStep = steps[activeIndex];\n const isLastStep = activeIndex === steps.length - 1;\n const isVertical = orientation === WIZARD_ORIENTATION.vertical;\n const isSelectable = typeof onStepSelect === 'function';\n const isBackHidden = hideBackOnFirstStep && activeIndex === 0;\n\n // el título de un paso: con onStepSelect es un botón (se pulsa y se llega con Tab); sin él, solo informa\n const stepTitleOf = (step, index) => {\n let caption;\n if (skippedKeys.has(step.key)) {\n caption = t('wizard.skipped');\n } else if (step.optional) {\n caption = t('wizard.optional');\n }\n const optional = caption && <Typography variant=\"caption\" color=\"text.secondary\">{caption}</Typography>;\n if (isSelectable) {\n return <StepButton optional={optional} onClick={() => onStepSelect(index)}>{step.label}</StepButton>;\n }\n return <StepLabel optional={optional}>{step.label}</StepLabel>;\n };\n\n // los botones del paso abierto: en fila van abajo de los campos; en vertical, dentro del paso\n const actions = (\n // sin «Anterior», los de seguir se quedan a la derecha (solos, space-between los mandaba a la izquierda; un margen\n // automático no sirve: Stack les pone margin 0 a sus hijos con más fuerza)\n <WizardActions direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5} ownSx={{justifyContent: isBackHidden ? 'flex-end' : 'space-between'}}>\n {!isBackHidden && (\n <Button variant=\"text\" color=\"inherit\" disabled={activeIndex === 0 || isSubmitting} onClick={onBack}>\n {t('wizard.back')}\n </Button>\n )}\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5}>\n {activeStep.optional && !isLastStep && onSkip && (\n <Button variant=\"text\" onClick={onSkip}>{t('wizard.skip')}</Button>\n )}\n {isLastStep\n ? submitButton\n : <Button variant=\"contained\" size=\"large\" onClick={onNext}>{t('wizard.next')}</Button>}\n </Stack>\n </WizardActions>\n );\n const completedOf = (step, index) => isStepCompleted(step, {index, activeIndex, skippedKeys, isSelectable});\n\n if (isVertical) {\n return (\n // nonLinear: con los títulos que se pulsan, MUI no debe apagar los pasos de adelante\n <WizardStepper activeStep={activeIndex} orientation=\"vertical\" nonLinear={isSelectable}>\n {steps.map((step, index) => (\n <Step key={step.key} completed={completedOf(step, index)}>\n {stepTitleOf(step, index)}\n {/* solo el paso abierto tiene sus campos montados: al cerrarse se desmontan (unmountOnExit). Los botones\n van solo en el abierto: el que se cierra (la animación dura un momento) ya no es el paso de esos\n botones, y un «Guardar» repetido confunde */}\n <StepContent slotProps={{transition: {unmountOnExit: true}}}>\n <Stack spacing={3} sx={{pt: 1}}>\n <Box>{step.content}</Box>\n {index === activeIndex && isLastStep && problemAlert}\n {index === activeIndex && actions}\n </Stack>\n </StepContent>\n </Step>\n ))}\n </WizardStepper>\n );\n }\n\n return (\n <Stack spacing={3}>\n {/* en el teléfono los nombres de los pasos no caben: se dice en qué paso va */}\n <WizardStepper activeStep={activeIndex} alternativeLabel nonLinear={isSelectable} ownSx={{display: {xs: 'none', sm: 'flex'}}}>\n {steps.map((step, index) => (\n <Step key={step.key} completed={completedOf(step, index)}>\n {stepTitleOf(step, index)}\n </Step>\n ))}\n </WizardStepper>\n <WizardPhoneSteps ownSx={{display: {xs: 'block', sm: 'none'}}}>\n <Typography variant=\"overline\" color=\"text.secondary\">{t('wizard.stepOf', {current: activeIndex + 1, total: steps.length})}</Typography>\n <Typography variant=\"h6\">{activeStep.label}</Typography>\n </WizardPhoneSteps>\n\n <Box>{activeStep.content}</Box>\n\n {isLastStep && problemAlert}\n\n {actions}\n </Stack>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst WizardLayout = withPieceSlots(WizardLayoutContent, THEME_NAME);\nexport default WizardLayout;\n"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createContext, useContext } from "react";
|
|
3
|
-
const FormSectionContext = createContext(
|
|
4
|
-
|
|
3
|
+
const FormSectionContext = createContext(
|
|
4
|
+
/** @type {false|{labelPlacement: string}} */
|
|
5
|
+
false
|
|
6
|
+
);
|
|
7
|
+
const useIsInsideFormSection = () => Boolean(useContext(FormSectionContext));
|
|
8
|
+
const useFormSectionLayout = () => useContext(FormSectionContext);
|
|
5
9
|
export {
|
|
6
10
|
FormSectionContext,
|
|
11
|
+
useFormSectionLayout,
|
|
7
12
|
useIsInsideFormSection
|
|
8
13
|
};
|
|
9
14
|
//# sourceMappingURL=formLayoutContext.js.map
|
|
@@ -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// 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\nexport const FormSectionContext = createContext(false);\n\n/** true si el campo va dentro de una FormSection. */\nexport const useIsInsideFormSection = () => useContext(FormSectionContext);\n"],"names":[],"mappings":";;
|
|
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;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const REQUIRED_LABEL_ATTRIBUTE = "data-required";
|
|
2
|
+
function cssStringOf(mark) {
|
|
3
|
+
return `"${String(mark).replace(/["\\]/g, "\\$&")}"`;
|
|
4
|
+
}
|
|
5
|
+
function requiredMarkSxOf(mark) {
|
|
6
|
+
const text = cssStringOf(mark);
|
|
7
|
+
return {
|
|
8
|
+
"&::after": { content: text, marginLeft: "0.15em" },
|
|
9
|
+
// con texto alterno vacío: se ve, pero el lector de pantalla no lo lee
|
|
10
|
+
[`@supports (content: ${text} / "")`]: { "&::after": { content: `${text} / ""` } }
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
REQUIRED_LABEL_ATTRIBUTE,
|
|
15
|
+
requiredMarkSxOf
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=requiredMark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requiredMark.js","sources":["../../src/inputs/requiredMark.js"],"sourcesContent":["// El asterisco de lo obligatorio en la etiqueta de un campo, compartido por FieldFrame (la etiqueta de arriba) y FormRow (el\n// renglón del formulario agrupado).\n//\n// OJO, por qué va en el estilo (::after) y no como texto dentro de la etiqueta: el texto de la etiqueta es como las pruebas\n// y los lectores encuentran el campo. Con «Nombre*» dentro, getByLabel('Nombre', {exact: true}) de Playwright y\n// getByLabelText('Nombre') de Testing Library dejaban de encontrarlo: subir de versión\n// rompía las pruebas de cualquier proyecto que ya usaba required. En ::after el texto sigue igual, y con «/ \"\"» el lector de\n// pantalla no dice «asterisco» (el campo ya dice que es obligatorio con required o aria-required). Un navegador viejo que no\n// conoce «/ \"\"» pinta el asterisco solo (@supports).\n//\n// La etiqueta lleva data-required: así el tema del proyecto la encuentra (label: {'&[data-required]::after': {color: …}}).\n//\n// Funciones puras, sin React.\n\n/** El atributo que marca la etiqueta de un campo obligatorio. */\nexport const REQUIRED_LABEL_ATTRIBUTE = 'data-required';\n\n/**\n * El texto de la marca como cadena de CSS (entre comillas, con las comillas y diagonales de adentro escapadas).\n *\n * @param {string} mark\n * @returns {string}\n */\nfunction cssStringOf(mark) {\n return `\"${String(mark).replace(/[\"\\\\]/g, '\\\\$&')}\"`;\n}\n\n/**\n * Los estilos de la etiqueta de un campo obligatorio: la marca después del texto, pegada y del mismo color.\n *\n * @param {string} mark el texto de la marca, ya traducido (common.form.requiredMark: «*»)\n * @returns {object} para el sx de la etiqueta\n */\nexport function requiredMarkSxOf(mark) {\n const text = cssStringOf(mark);\n return {\n '&::after': {content: text, marginLeft: '0.15em'},\n // con texto alterno vacío: se ve, pero el lector de pantalla no lo lee\n [`@supports (content: ${text} / \"\")`]: {'&::after': {content: `${text} / \"\"`}},\n };\n}\n"],"names":[],"mappings":"AAeY,MAAC,2BAA2B;AAQxC,SAAS,YAAY,MAAM;AACvB,SAAO,IAAI,OAAO,IAAI,EAAE,QAAQ,UAAU,MAAM,CAAC;AACrD;AAQO,SAAS,iBAAiB,MAAM;AACnC,QAAM,OAAO,YAAY,IAAI;AAC7B,SAAO;AAAA,IACH,YAAY,EAAC,SAAS,MAAM,YAAY,SAAQ;AAAA;AAAA,IAEhD,CAAC,uBAAuB,IAAI,QAAQ,GAAG,EAAC,YAAY,EAAC,SAAS,GAAG,IAAI,QAAO,EAAC;AAAA,EACrF;AACA;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
|
+
import { openingStepOf, WIZARD_STEP_SELECTION, stepSelectionActionOf } from "./wizardSteps.js";
|
|
4
|
+
import { createLogger } from "../observability/logger.js";
|
|
5
|
+
const logger = createLogger("FormWizard");
|
|
6
|
+
function useWizardSteps({ steps, initialStepKey, stepSelection = WIZARD_STEP_SELECTION.none, onStepChange, isStepValid }) {
|
|
7
|
+
const [opening] = useState(() => openingStepOf(steps, initialStepKey));
|
|
8
|
+
const [activeIndex, setActiveIndex] = useState(opening.index);
|
|
9
|
+
const announcedIndex = useRef(opening.index);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (opening.isUnknownKey) {
|
|
12
|
+
logger.warn(`initialStepKey «${initialStepKey}» no es de ningún paso: se abrió el primero`, { stepKeys: steps.map((step) => step.key) });
|
|
13
|
+
}
|
|
14
|
+
}, []);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (announcedIndex.current !== activeIndex) {
|
|
17
|
+
announcedIndex.current = activeIndex;
|
|
18
|
+
onStepChange?.(steps[activeIndex].key, activeIndex);
|
|
19
|
+
}
|
|
20
|
+
}, [activeIndex, onStepChange, steps]);
|
|
21
|
+
const selectStep = async (index) => {
|
|
22
|
+
const action = stepSelectionActionOf(stepSelection, activeIndex, index);
|
|
23
|
+
if (action === "ignore") {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (action === "checkFirst" && !await isStepValid(activeIndex)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setActiveIndex(index);
|
|
30
|
+
};
|
|
31
|
+
return { activeIndex, setActiveIndex, selectStep: stepSelection === WIZARD_STEP_SELECTION.none ? void 0 : selectStep };
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
useWizardSteps
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=useWizardSteps.js.map
|
|
@@ -0,0 +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;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const WIZARD_ORIENTATION = Object.freeze({ horizontal: "horizontal", vertical: "vertical" });
|
|
2
|
+
const WIZARD_STEP_SELECTION = Object.freeze({ none: "none", free: "free", checked: "checked" });
|
|
3
|
+
function openingStepOf(steps, stepKey) {
|
|
4
|
+
if (stepKey === void 0 || stepKey === null) {
|
|
5
|
+
return { index: 0, isUnknownKey: false };
|
|
6
|
+
}
|
|
7
|
+
const index = steps.findIndex((step) => step.key === stepKey);
|
|
8
|
+
return index >= 0 ? { index, isUnknownKey: false } : { index: 0, isUnknownKey: true };
|
|
9
|
+
}
|
|
10
|
+
function stepSelectionActionOf(stepSelection, activeIndex, targetIndex) {
|
|
11
|
+
if (stepSelection === WIZARD_STEP_SELECTION.none || targetIndex === activeIndex) {
|
|
12
|
+
return "ignore";
|
|
13
|
+
}
|
|
14
|
+
return stepSelection === WIZARD_STEP_SELECTION.checked ? "checkFirst" : "jump";
|
|
15
|
+
}
|
|
16
|
+
function isStepCompleted(step, { index, activeIndex, skippedKeys, isSelectable }) {
|
|
17
|
+
if (typeof step.completed === "boolean") {
|
|
18
|
+
return step.completed;
|
|
19
|
+
}
|
|
20
|
+
if (isSelectable || skippedKeys.has(step.key)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return index < activeIndex;
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
WIZARD_ORIENTATION,
|
|
27
|
+
WIZARD_STEP_SELECTION,
|
|
28
|
+
isStepCompleted,
|
|
29
|
+
openingStepOf,
|
|
30
|
+
stepSelectionActionOf
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=wizardSteps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wizardSteps.js","sources":["../../src/inputs/wizardSteps.js"],"sourcesContent":["// Las reglas del formulario por pasos que no dependen de formik, de react-hook-form ni de React: cómo se acomodan los pasos,\n// qué pasa al pulsar el título de otro paso y en qué paso se abre. Las usan WizardLayout y los dos FormWizard.\n//\n// Por qué existen: editar un registro de varios pasos (un expediente, un perfil) casi nunca es recorrerlos todos; se entra a\n// corregir un dato suelto y el servidor dice en qué paso está. Con «Siguiente» como único camino, eso es el camino largo.\n//\n// Funciones puras, sin React: function (regla de la casa). Pruebas en wizardSteps.test.js.\n\n/**\n * Cómo se acomodan los pasos:\n * horizontal los pasos en fila arriba y los campos abajo (lo de siempre; en el teléfono, «Paso 2 de 4» y su nombre);\n * vertical uno debajo del otro, como un acordeón: los campos y los botones van dentro del paso que está abierto.\n */\nexport const WIZARD_ORIENTATION = Object.freeze({horizontal: 'horizontal', vertical: 'vertical'});\n\n/**\n * Qué pasa al pulsar el título de otro paso:\n * none los títulos no se pulsan (lo de siempre): se avanza con «Siguiente»;\n * free salta directo, sin revisar el paso en el que está; al enviar, el formulario regresa al primer paso con error;\n * checked salta solo si el paso en el que está no tiene errores (igual que «Siguiente»).\n */\nexport const WIZARD_STEP_SELECTION = Object.freeze({none: 'none', free: 'free', checked: 'checked'});\n\n/**\n * El paso en el que se abre: el de esa llave (la que dice el servidor) o el primero si no llega.\n *\n * @param {Array<{key: string}>} steps\n * @param {string} [stepKey]\n * @returns {{index: number, isUnknownKey: boolean}} isUnknownKey: llegó una llave que no es de ningún paso (se abre el primero)\n */\nexport function openingStepOf(steps, stepKey) {\n if (stepKey === undefined || stepKey === null) {\n return {index: 0, isUnknownKey: false};\n }\n const index = steps.findIndex((step) => step.key === stepKey);\n return index >= 0 ? {index, isUnknownKey: false} : {index: 0, isUnknownKey: true};\n}\n\n/**\n * ¿Al pulsar el título del paso `targetIndex` hay que revisar antes el paso en el que está? Solo con `checked` y si de veras\n * cambia de paso. Pulsar el paso en el que ya está no hace nada.\n *\n * @param {string} stepSelection uno de WIZARD_STEP_SELECTION\n * @param {number} activeIndex\n * @param {number} targetIndex\n * @returns {'ignore'|'jump'|'checkFirst'}\n */\nexport function stepSelectionActionOf(stepSelection, activeIndex, targetIndex) {\n if (stepSelection === WIZARD_STEP_SELECTION.none || targetIndex === activeIndex) {\n return 'ignore';\n }\n return stepSelection === WIZARD_STEP_SELECTION.checked ? 'checkFirst' : 'jump';\n}\n\n/**\n * ¿El paso se pinta como hecho (la palomita)? En fila y sin pulsar, los de antes del que está abierto, salvo los omitidos\n * (lo de siempre). Cuando los títulos se pulsan, «antes» ya no quiere decir «hecho» (se pudo brincar): sin palomita, salvo\n * que la pantalla lo diga en `steps[].completed` (sabe qué datos ya están completos).\n *\n * @param {{key: string, completed?: boolean}} step\n * @param {object} place\n * @param {number} place.index\n * @param {number} place.activeIndex\n * @param {Set<string>} place.skippedKeys\n * @param {boolean} place.isSelectable\n * @returns {boolean}\n */\nexport function isStepCompleted(step, {index, activeIndex, skippedKeys, isSelectable}) {\n if (typeof step.completed === 'boolean') {\n return step.completed;\n }\n if (isSelectable || skippedKeys.has(step.key)) {\n return false;\n }\n return index < activeIndex;\n}\n"],"names":[],"mappings":"AAaY,MAAC,qBAAqB,OAAO,OAAO,EAAC,YAAY,cAAc,UAAU,WAAU,CAAC;AAQpF,MAAC,wBAAwB,OAAO,OAAO,EAAC,MAAM,QAAQ,MAAM,QAAQ,SAAS,UAAS,CAAC;AAS5F,SAAS,cAAc,OAAO,SAAS;AAC1C,MAAI,YAAY,UAAa,YAAY,MAAM;AAC3C,WAAO,EAAC,OAAO,GAAG,cAAc,MAAK;AAAA,EACzC;AACA,QAAM,QAAQ,MAAM,UAAU,CAAC,SAAS,KAAK,QAAQ,OAAO;AAC5D,SAAO,SAAS,IAAI,EAAC,OAAO,cAAc,MAAK,IAAI,EAAC,OAAO,GAAG,cAAc,KAAI;AACpF;AAWO,SAAS,sBAAsB,eAAe,aAAa,aAAa;AAC3E,MAAI,kBAAkB,sBAAsB,QAAQ,gBAAgB,aAAa;AAC7E,WAAO;AAAA,EACX;AACA,SAAO,kBAAkB,sBAAsB,UAAU,eAAe;AAC5E;AAeO,SAAS,gBAAgB,MAAM,EAAC,OAAO,aAAa,aAAa,aAAY,GAAG;AACnF,MAAI,OAAO,KAAK,cAAc,WAAW;AACrC,WAAO,KAAK;AAAA,EAChB;AACA,MAAI,gBAAgB,YAAY,IAAI,KAAK,GAAG,GAAG;AAC3C,WAAO;AAAA,EACX;AACA,SAAO,QAAQ;AACnB;"}
|
package/dist/locales/en.js
CHANGED