@stackcraftmx/calcita 0.32.1 → 0.47.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 +263 -2
- package/cli/packageVersions.js +12 -3
- package/cli/projectFiles.js +5 -2
- package/cli/templates/module/__Name__Page.test.jsx.tpl +24 -0
- package/cli/templates/project/README.md.tpl +14 -1
- package/cli/templates/project/src/app/appRouter.jsx.tpl +5 -2
- package/cli/templates/project/src/core/accountConnector.js.tpl +4 -2
- package/cli/templates/project/src/modules/auth/SignInPage.test.jsx.tpl +20 -0
- package/cli/templates/project/src/modules/home/HomePage.test.jsx.tpl +16 -0
- package/cli/templates/project/src/testing/browserGaps.js.tpl +70 -0
- package/cli/templates/project/src/testing/openScreen.jsx.tpl +58 -0
- package/cli/templates/project/src/testing/setup.js.tpl +25 -0
- package/cli/templates/project/vite.config.js.tpl +12 -0
- package/dist/DataTable/GoToPage.js +69 -13
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +2 -0
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/ServerTableFilterDrawer.js +52 -0
- package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -0
- package/dist/DataTable/filterContext.js.map +1 -1
- package/dist/DataTable/goToPageOptions.js +17 -0
- package/dist/DataTable/goToPageOptions.js.map +1 -0
- package/dist/date-pickers.js +28 -2
- package/dist/date-pickers.js.map +1 -1
- package/dist/datePickers/DateInput.js +15 -34
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +75 -0
- package/dist/datePickers/DateTimeInput.js.map +1 -0
- package/dist/datePickers/FormDateTimeField.js +12 -0
- package/dist/datePickers/FormDateTimeField.js.map +1 -0
- package/dist/datePickers/FormMonthField.js +12 -0
- package/dist/datePickers/FormMonthField.js.map +1 -0
- package/dist/datePickers/FormTimeField.js +12 -0
- package/dist/datePickers/FormTimeField.js.map +1 -0
- package/dist/datePickers/MonthInput.js +58 -0
- package/dist/datePickers/MonthInput.js.map +1 -0
- package/dist/datePickers/PickerFrame.js +27 -0
- package/dist/datePickers/PickerFrame.js.map +1 -0
- package/dist/datePickers/TimeInput.js +55 -0
- package/dist/datePickers/TimeInput.js.map +1 -0
- package/dist/datePickers/dateTimeRules.js +24 -0
- package/dist/datePickers/dateTimeRules.js.map +1 -0
- package/dist/datePickers/pickerValues.js +106 -0
- package/dist/datePickers/pickerValues.js.map +1 -0
- package/dist/datePickers/useCurrentMinute.js +28 -0
- package/dist/datePickers/useCurrentMinute.js.map +1 -0
- package/dist/datePickers/usePickerField.js +22 -0
- package/dist/datePickers/usePickerField.js.map +1 -0
- package/dist/dictionaries/dictionaries.js +3 -1
- package/dist/dictionaries/dictionaries.js.map +1 -1
- package/dist/forms/FormAttachmentsField.js +12 -0
- package/dist/forms/FormAttachmentsField.js.map +1 -0
- package/dist/forms/FormCheckboxGroupField.js +12 -0
- package/dist/forms/FormCheckboxGroupField.js.map +1 -0
- package/dist/forms/FormFileField.js +12 -0
- package/dist/forms/FormFileField.js.map +1 -0
- package/dist/forms/FormFileUploadField.js +12 -0
- package/dist/forms/FormFileUploadField.js.map +1 -0
- package/dist/forms/FormImageField.js +12 -0
- package/dist/forms/FormImageField.js.map +1 -0
- package/dist/forms/FormMaskedField.js +12 -0
- package/dist/forms/FormMaskedField.js.map +1 -0
- package/dist/forms/FormRepeatingFields.js +54 -0
- package/dist/forms/FormRepeatingFields.js.map +1 -0
- package/dist/forms/FormTransferListField.js +12 -0
- package/dist/forms/FormTransferListField.js.map +1 -0
- package/dist/forms/applyServerProblem.js +10 -3
- package/dist/forms/applyServerProblem.js.map +1 -1
- package/dist/forms/listRules.js +111 -0
- package/dist/forms/listRules.js.map +1 -0
- package/dist/forms/serverProblemFields.js +17 -0
- package/dist/forms/serverProblemFields.js.map +1 -1
- package/dist/forms.js +51 -29
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormAttachmentsField.js +12 -0
- package/dist/hookForm/FormAttachmentsField.js.map +1 -0
- package/dist/hookForm/FormCheckboxGroupField.js +12 -0
- package/dist/hookForm/FormCheckboxGroupField.js.map +1 -0
- package/dist/hookForm/FormFileField.js +12 -0
- package/dist/hookForm/FormFileField.js.map +1 -0
- package/dist/hookForm/FormFileUploadField.js +12 -0
- package/dist/hookForm/FormFileUploadField.js.map +1 -0
- package/dist/hookForm/FormImageField.js +12 -0
- package/dist/hookForm/FormImageField.js.map +1 -0
- package/dist/hookForm/FormMaskedField.js +12 -0
- package/dist/hookForm/FormMaskedField.js.map +1 -0
- package/dist/hookForm/FormRepeatingFields.js +44 -0
- package/dist/hookForm/FormRepeatingFields.js.map +1 -0
- package/dist/hookForm/FormTransferListField.js +12 -0
- package/dist/hookForm/FormTransferListField.js.map +1 -0
- package/dist/hookForm/applyServerProblem.js +8 -3
- package/dist/hookForm/applyServerProblem.js.map +1 -1
- package/dist/index.js +188 -100
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +74 -7
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +100 -0
- package/dist/inputs/CheckboxGroupInput.js.map +1 -0
- package/dist/inputs/MaskedInput.js +86 -0
- package/dist/inputs/MaskedInput.js.map +1 -0
- package/dist/inputs/NumberInput.js +12 -1
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/RepeatingFields.js +177 -0
- package/dist/inputs/RepeatingFields.js.map +1 -0
- package/dist/inputs/SelectInput.js +43 -5
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TransferListInput.js +215 -0
- package/dist/inputs/TransferListInput.js.map +1 -0
- package/dist/inputs/inputMasks.js +84 -0
- package/dist/inputs/inputMasks.js.map +1 -0
- package/dist/inputs/selectAllState.js +20 -0
- package/dist/inputs/selectAllState.js.map +1 -0
- package/dist/inputs/useRowKeys.js +26 -0
- package/dist/inputs/useRowKeys.js.map +1 -0
- package/dist/legal/CookieNotice.js +79 -0
- package/dist/legal/CookieNotice.js.map +1 -0
- package/dist/legal/TermsAcceptanceDialog.js +129 -0
- package/dist/legal/TermsAcceptanceDialog.js.map +1 -0
- package/dist/legal/consentStorage.js +18 -0
- package/dist/legal/consentStorage.js.map +1 -0
- package/dist/legal/useCookieConsent.js +17 -0
- package/dist/legal/useCookieConsent.js.map +1 -0
- package/dist/locales/en.js +233 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +233 -1
- package/dist/locales/es.js.map +1 -1
- package/dist/mexico/CfdiSummary.js +81 -0
- package/dist/mexico/CfdiSummary.js.map +1 -0
- package/dist/mexico/EfirmaFields.js +167 -0
- package/dist/mexico/EfirmaFields.js.map +1 -0
- package/dist/mexico/certificateReader.js +79 -0
- package/dist/mexico/certificateReader.js.map +1 -0
- package/dist/mexico/cfdiReader.js +81 -0
- package/dist/mexico/cfdiReader.js.map +1 -0
- package/dist/mexico/derReader.js +81 -0
- package/dist/mexico/derReader.js.map +1 -0
- package/dist/mexico/efirmaKey.js +51 -0
- package/dist/mexico/efirmaKey.js.map +1 -0
- package/dist/mexico/mexicoIdentifiers.js +86 -0
- package/dist/mexico/mexicoIdentifiers.js.map +1 -0
- package/dist/mexico/mexicoRules.js +16 -0
- package/dist/mexico/mexicoRules.js.map +1 -0
- package/dist/mexico.js +32 -0
- package/dist/mexico.js.map +1 -0
- package/dist/react-hook-form.js +40 -17
- package/dist/react-hook-form.js.map +1 -1
- package/dist/rich-text.js +19 -0
- package/dist/rich-text.js.map +1 -0
- package/dist/richText/FormRichTextField.js +12 -0
- package/dist/richText/FormRichTextField.js.map +1 -0
- package/dist/richText/RichTextEditor.js +108 -0
- package/dist/richText/RichTextEditor.js.map +1 -0
- package/dist/richText/RichTextLinkPopover.js +57 -0
- package/dist/richText/RichTextLinkPopover.js.map +1 -0
- package/dist/richText/RichTextToolbar.js +189 -0
- package/dist/richText/RichTextToolbar.js.map +1 -0
- package/dist/richText/RichTextView.js +26 -0
- package/dist/richText/RichTextView.js.map +1 -0
- package/dist/richText/richTextContentSx.js +42 -0
- package/dist/richText/richTextContentSx.js.map +1 -0
- package/dist/richText/richTextExtensions.js +44 -0
- package/dist/richText/richTextExtensions.js.map +1 -0
- package/dist/richText/richTextRules.js +46 -0
- package/dist/richText/richTextRules.js.map +1 -0
- package/dist/security/SessionsCard.js +11 -7
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/table.js +2 -0
- package/dist/table.js.map +1 -1
- package/dist/theme/components/glassComponents.js +27 -0
- package/dist/theme/components/glassComponents.js.map +1 -1
- package/dist/theme/components/minimalComponents.js +12 -0
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +21 -0
- package/dist/types/DataTable/filterContext.d.ts +2 -1
- package/dist/types/DataTable/goToPageOptions.d.ts +15 -0
- package/dist/types/DataTable/index.d.ts +1 -0
- package/dist/types/date-pickers.d.ts +10 -1
- package/dist/types/datePickers/DateInput.d.ts +6 -4
- package/dist/types/datePickers/DateTimeInput.d.ts +50 -0
- package/dist/types/datePickers/FormDateTimeField.d.ts +7 -0
- package/dist/types/datePickers/FormMonthField.d.ts +7 -0
- package/dist/types/datePickers/FormTimeField.d.ts +7 -0
- package/dist/types/datePickers/MonthInput.d.ts +39 -0
- package/dist/types/datePickers/PickerFrame.d.ts +22 -0
- package/dist/types/datePickers/TimeInput.d.ts +38 -0
- package/dist/types/datePickers/dateTimeRules.d.ts +31 -0
- package/dist/types/datePickers/pickerValues.d.ts +114 -0
- package/dist/types/datePickers/useCurrentMinute.d.ts +5 -0
- package/dist/types/datePickers/usePickerField.d.ts +24 -0
- package/dist/types/dictionaries/dictionaries.d.ts +6 -3
- package/dist/types/forms/FormAttachmentsField.d.ts +7 -0
- package/dist/types/forms/FormCheckboxGroupField.d.ts +7 -0
- package/dist/types/forms/FormFileField.d.ts +7 -0
- package/dist/types/forms/FormFileUploadField.d.ts +7 -0
- package/dist/types/forms/FormImageField.d.ts +7 -0
- package/dist/types/forms/FormMaskedField.d.ts +7 -0
- package/dist/types/forms/FormRepeatingFields.d.ts +25 -0
- package/dist/types/forms/FormTransferListField.d.ts +7 -0
- package/dist/types/forms/index.d.ts +10 -1
- package/dist/types/forms/listRules.d.ts +48 -0
- package/dist/types/forms/serverProblemFields.d.ts +13 -0
- package/dist/types/forms.d.ts +1 -1
- package/dist/types/hookForm/FormAttachmentsField.d.ts +8 -0
- package/dist/types/hookForm/FormCheckboxGroupField.d.ts +8 -0
- package/dist/types/hookForm/FormFileField.d.ts +8 -0
- package/dist/types/hookForm/FormFileUploadField.d.ts +8 -0
- package/dist/types/hookForm/FormImageField.d.ts +8 -0
- package/dist/types/hookForm/FormMaskedField.d.ts +9 -0
- package/dist/types/hookForm/FormRepeatingFields.d.ts +27 -0
- package/dist/types/hookForm/FormTransferListField.d.ts +8 -0
- package/dist/types/index.d.ts +25 -3
- package/dist/types/inputs/CatalogInput.d.ts +7 -1
- package/dist/types/inputs/CheckboxGroupInput.d.ts +39 -0
- package/dist/types/inputs/MaskedInput.d.ts +35 -0
- package/dist/types/inputs/RepeatingFields.d.ts +52 -0
- package/dist/types/inputs/SelectInput.d.ts +6 -1
- package/dist/types/inputs/TransferListInput.d.ts +43 -0
- package/dist/types/inputs/inputMasks.d.ts +52 -0
- package/dist/types/inputs/selectAllState.d.ts +11 -0
- package/dist/types/inputs/useRowKeys.d.ts +11 -0
- package/dist/types/legal/CookieNotice.d.ts +30 -0
- package/dist/types/legal/TermsAcceptanceDialog.d.ts +32 -0
- package/dist/types/legal/consentStorage.d.ts +16 -0
- package/dist/types/legal/useCookieConsent.d.ts +13 -0
- package/dist/types/locales/en.d.ts +232 -0
- package/dist/types/locales/es.d.ts +232 -0
- package/dist/types/mexico/CfdiSummary.d.ts +17 -0
- package/dist/types/mexico/EfirmaFields.d.ts +38 -0
- package/dist/types/mexico/certificateReader.d.ts +37 -0
- package/dist/types/mexico/cfdiReader.d.ts +95 -0
- package/dist/types/mexico/derReader.d.ts +54 -0
- package/dist/types/mexico/efirmaKey.d.ts +26 -0
- package/dist/types/mexico/mexicoIdentifiers.d.ts +61 -0
- package/dist/types/mexico/mexicoRules.d.ts +29 -0
- package/dist/types/mexico.d.ts +11 -0
- package/dist/types/react-hook-form.d.ts +10 -1
- package/dist/types/rich-text.d.ts +5 -0
- package/dist/types/richText/FormRichTextField.d.ts +7 -0
- package/dist/types/richText/RichTextEditor.d.ts +42 -0
- package/dist/types/richText/RichTextLinkPopover.d.ts +17 -0
- package/dist/types/richText/RichTextToolbar.d.ts +15 -0
- package/dist/types/richText/RichTextView.d.ts +13 -0
- package/dist/types/richText/richTextContentSx.d.ts +7 -0
- package/dist/types/richText/richTextExtensions.d.ts +15 -0
- package/dist/types/richText/richTextRules.d.ts +41 -0
- package/dist/types/table.d.ts +1 -1
- package/dist/types/ui/actions/ActionsMenu.d.ts +18 -3
- package/dist/types/ui/actions/CopyButton.d.ts +7 -4
- package/dist/types/ui/actions/tableRowText.d.ts +6 -0
- package/dist/types/ui/cards/RecordCard.d.ts +65 -0
- package/dist/types/ui/cards/RecordCardSkeleton.d.ts +17 -0
- package/dist/types/ui/countdown/Countdown.d.ts +31 -0
- package/dist/types/ui/countdown/countdownParts.d.ts +39 -0
- package/dist/types/ui/countdown/useCountdown.d.ts +14 -0
- package/dist/types/ui/documents/PdfViewerDialog.d.ts +21 -0
- package/dist/types/ui/filters/FilterDrawer.d.ts +34 -0
- package/dist/types/ui/filters/filterDrawerFields.d.ts +139 -0
- package/dist/types/ui/help/HelpButton.d.ts +15 -0
- package/dist/types/ui/help/HelpCarouselDialog.d.ts +35 -0
- package/dist/types/ui/help/useHelpCarousel.d.ts +21 -0
- package/dist/types/ui/index.d.ts +10 -0
- package/dist/types/ui/labels/IconBadge.d.ts +34 -0
- package/dist/types/ui/navigation/RouteTabs.d.ts +44 -0
- package/dist/types/ui/navigation/routeTabMatching.d.ts +27 -0
- package/dist/types/upload/AttachmentsInput.d.ts +62 -0
- package/dist/types/upload/FileChip.d.ts +18 -0
- package/dist/types/upload/FileDropzone.d.ts +3 -0
- package/dist/types/upload/FileInput.d.ts +48 -0
- package/dist/types/upload/FileUploadInput.d.ts +80 -0
- package/dist/types/upload/ImageInput.d.ts +53 -0
- package/dist/types/upload/TemplateDownloadButton.d.ts +39 -0
- package/dist/types/upload/fileAppearance.d.ts +8 -0
- package/dist/types/upload/fileRules.d.ts +38 -0
- package/dist/types/upload/transferProgress.d.ts +18 -0
- package/dist/types/upload/uploadMachine.d.ts +6 -2
- package/dist/types/upload/uploadWithProgress.d.ts +6 -2
- package/dist/types/upload/useFilePicker.d.ts +39 -0
- package/dist/ui/actions/ActionsMenu.js +98 -15
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/CopyButton.js +31 -11
- package/dist/ui/actions/CopyButton.js.map +1 -1
- package/dist/ui/actions/tableRowText.js +7 -0
- package/dist/ui/actions/tableRowText.js.map +1 -0
- package/dist/ui/cards/RecordCard.js +145 -0
- package/dist/ui/cards/RecordCard.js.map +1 -0
- package/dist/ui/cards/RecordCardSkeleton.js +33 -0
- package/dist/ui/cards/RecordCardSkeleton.js.map +1 -0
- package/dist/ui/countdown/Countdown.js +38 -0
- package/dist/ui/countdown/Countdown.js.map +1 -0
- package/dist/ui/countdown/countdownParts.js +34 -0
- package/dist/ui/countdown/countdownParts.js.map +1 -0
- package/dist/ui/countdown/useCountdown.js +35 -0
- package/dist/ui/countdown/useCountdown.js.map +1 -0
- package/dist/ui/documents/PdfViewerDialog.js +81 -0
- package/dist/ui/documents/PdfViewerDialog.js.map +1 -0
- package/dist/ui/filters/FilterDrawer.js +211 -0
- package/dist/ui/filters/FilterDrawer.js.map +1 -0
- package/dist/ui/filters/filterDrawerFields.js +119 -0
- package/dist/ui/filters/filterDrawerFields.js.map +1 -0
- package/dist/ui/help/HelpButton.js +17 -0
- package/dist/ui/help/HelpButton.js.map +1 -0
- package/dist/ui/help/HelpCarouselDialog.js +154 -0
- package/dist/ui/help/HelpCarouselDialog.js.map +1 -0
- package/dist/ui/help/useHelpCarousel.js +25 -0
- package/dist/ui/help/useHelpCarousel.js.map +1 -0
- package/dist/ui/labels/IconBadge.js +82 -0
- package/dist/ui/labels/IconBadge.js.map +1 -0
- package/dist/ui/navigation/RouteTabs.js +75 -0
- package/dist/ui/navigation/RouteTabs.js.map +1 -0
- package/dist/ui/navigation/routeTabMatching.js +23 -0
- package/dist/ui/navigation/routeTabMatching.js.map +1 -0
- package/dist/upload/AttachmentsInput.js +127 -0
- package/dist/upload/AttachmentsInput.js.map +1 -0
- package/dist/upload/FileChip.js +27 -0
- package/dist/upload/FileChip.js.map +1 -0
- package/dist/upload/FileDropzone.js +19 -84
- package/dist/upload/FileDropzone.js.map +1 -1
- package/dist/upload/FileInput.js +112 -0
- package/dist/upload/FileInput.js.map +1 -0
- package/dist/upload/FileUploadInput.js +285 -0
- package/dist/upload/FileUploadInput.js.map +1 -0
- package/dist/upload/ImageInput.js +125 -0
- package/dist/upload/ImageInput.js.map +1 -0
- package/dist/upload/TemplateDownloadButton.js +67 -0
- package/dist/upload/TemplateDownloadButton.js.map +1 -0
- package/dist/upload/fileAppearance.js +21 -0
- package/dist/upload/fileAppearance.js.map +1 -0
- package/dist/upload/fileRules.js +27 -1
- package/dist/upload/fileRules.js.map +1 -1
- package/dist/upload/transferProgress.js +23 -0
- package/dist/upload/transferProgress.js.map +1 -0
- package/dist/upload/uploadMachine.js +8 -8
- package/dist/upload/uploadMachine.js.map +1 -1
- package/dist/upload/uploadWithProgress.js +1 -1
- package/dist/upload/uploadWithProgress.js.map +1 -1
- package/dist/upload/useFilePicker.js +101 -0
- package/dist/upload/useFilePicker.js.map +1 -0
- package/package.json +44 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectInput.js","sources":["../../src/inputs/SelectInput.jsx"],"sourcesContent":["// Lista desplegable suelta, con la etiqueta arriba (mismo look que TextInput): la que pintan FormSelectField (formik) y la\n// de react-hook-form, y la que se usa sin formulario.\n//\n// Cada opción puede traer una explicación (description) que se lee en la lista para escoger; en la caja queda solo el\n// nombre de lo elegido. OJO: sin renderValue, MUI copiaba la opción completa a la caja y se leía «MenudeoCompra por pieza…».\n//\n// Con `multiple` se eligen varias: la lista se queda abierta, cada elegida lleva su palomita y en la caja quedan sus nombres\n// separados por coma, en el orden de las opciones. Al formulario llega la lista de valores (Yup.array().min(1) la valida).\n// Para catálogos largos, CatalogInput con `multiple` (busca y deja chips).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSelectInput con defaultProps y\n// styleOverrides de sus partes: option, optionText.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport MenuItem from '@mui/material/MenuItem';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} 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.CalcitaSelectInput)\nconst THEME_NAME = 'CalcitaSelectInput';\nconst SelectOption = piecePart(THEME_NAME, 'Option', MenuItem);\nconst SelectOptionText = piecePart(THEME_NAME, 'OptionText', Box);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|Array<string|number>|null|undefined} props.value '' = sin elegir; con multiple, la lista ([] = ninguna)\n * @param {(value: any) => void} props.onChange recibe el valor elegido (con multiple, la lista), no el evento\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string}>} props.options\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] texto de la opción vacía\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias opciones\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SelectInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSelectInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, options, error, helperText = ' ', placeholder, disabled = false, inputRef, multiple = false, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'select'}-${generatedId}`;\n // OJO: la lista desplegable de MUI es un div y no un <input>: el `for` de la etiqueta no la une. Se nombra con su etiqueta\n // por aria-labelledby (labelId); sin esto el lector de pantalla no decía «Ciudad», solo lo elegido\n const labelId = `${inputId}-label`;\n const errorText = translateValidationMessage(t, error);\n const placeholderText = (\n <Typography component=\"span\"\n // OJO: el placeholder de un select activo se lee (text.secondary); text.disabled solo con el campo inactivo\n color={disabled ? 'text.disabled' : 'text.secondary'}>{placeholder ?? t('form.selectPlaceholder')}</Typography>\n );\n const renderSelectedValue = (selectedValue) => {\n if (multiple) {\n const selectedLabels = options.filter((option) => selectedValue.includes(option.value)).map((option) => option.label);\n return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholderText;\n }\n return options.find((option) => option.value === selectedValue)?.label ?? placeholderText;\n };\n const currentValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? '');\n // OJO: con multiple, el autollenado del navegador puede mandar el valor como texto separado por comas (lo dice MUI)\n const handleChange = (event) => {\n const nextValue = event.target.value;\n onChange(multiple && typeof nextValue === 'string' ? nextValue.split(',') : nextValue);\n };\n\n return (\n <FieldFrame inputId={inputId} labelId={labelId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n select\n size=\"small\"\n fullWidth\n disabled={disabled}\n value={currentValue}\n onChange={handleChange}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n slotProps={{select: {multiple, displayEmpty: true, renderValue: renderSelectedValue, labelId}}}>\n {/* la opción vacía solo con una: con varias, se quitan picándolas otra vez */}\n {!multiple && <MenuItem value=\"\" disabled>{placeholderText}</MenuItem>}\n {options.map((option) => (\n // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del\n // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)\n <SelectOption key={option.value} value={option.value} ownSx={{whiteSpace: 'normal'}}>\n <SelectOptionText component=\"span\" ownSx={{display: 'flex', flexDirection: 'column', minWidth: 0, flexGrow: 1}}>\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n {option.description && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n )}\n </SelectOptionText>\n </SelectOption>\n ))}\n </TextField>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,eAAe,UAAU,YAAY,UAAU,QAAQ;AAC7D,MAAM,mBAAmB,UAAU,YAAY,cAAc,GAAG;AAkBhE,SAAwB,YAAY,OAAO;AAEvC,QAAM;AAAA,IACN;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAa,WAAW;AAAA,IAAO;AAAA,IAAU,WAAW;AAAA,IAAO;AAAA,IACnI,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AAC/B,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,QAAQ,IAAI,WAAW;AAGlD,QAAM,UAAU,GAAG,OAAO;AAC1B,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,kBACF;AAAA,IAAC;AAAA,IAAA;AAAA,MAAW,WAAU;AAAA,MAEV,OAAO,WAAW,kBAAkB;AAAA,MAAmB,UAAA,eAAe,EAAE,wBAAwB;AAAA,IAAA;AAAA,EAAA;AAEhH,QAAM,sBAAsB,CAAC,kBAAkB;AAC3C,QAAI,UAAU;AACV,YAAM,iBAAiB,QAAQ,OAAO,CAAC,WAAW,cAAc,SAAS,OAAO,KAAK,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK;AACpH,aAAO,eAAe,SAAS,IAAI,eAAe,KAAK,IAAI,IAAI;AAAA,IACnE;AACA,WAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,aAAa,GAAG,SAAS;AAAA,EAC9E;AACA,QAAM,eAAe,WAAY,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAO,SAAS;AAEhF,QAAM,eAAe,CAAC,UAAU;AAC5B,UAAM,YAAY,MAAM,OAAO;AAC/B,aAAS,YAAY,OAAO,cAAc,WAAW,UAAU,MAAM,GAAG,IAAI,SAAS;AAAA,EACzF;AAEA,SACI,oBAAC,YAAA,EAAW,SAAkB,SAAkB,OAC5C,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA,QAAM;AAAA,MACN,MAAK;AAAA,MACL,WAAS;AAAA,MACT;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,MACV;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,aAAa;AAAA,MACzB,WAAW,EAAC,QAAQ,EAAC,UAAU,cAAc,MAAM,aAAa,qBAAqB,UAAO;AAAA,MAE3F,UAAA;AAAA,QAAA,CAAC,YAAY,oBAAC,UAAA,EAAS,OAAM,IAAG,UAAQ,MAAE,UAAA,iBAAgB;AAAA,QAC1D,QAAQ,IAAI,CAAC;AAAA;AAAA;AAAA,UAGV,oBAAC,cAAA,EAAgC,OAAO,OAAO,OAAO,OAAO,EAAC,YAAY,SAAA,GACtE,UAAA,qBAAC,kBAAA,EAAiB,WAAU,QAAO,OAAO,EAAC,SAAS,QAAQ,eAAe,UAAU,UAAU,GAAG,UAAU,EAAA,GACxG,UAAA;AAAA,YAAA,oBAAC,cAAW,WAAU,QAAO,SAAQ,SAAS,iBAAO,OAAM;AAAA,YAC1D,OAAO,eACJ,oBAAC,YAAA,EAAW,WAAU,QAAO,SAAQ,WAAU,OAAM,kBAAkB,UAAA,OAAO,YAAA,CAAY;AAAA,UAAA,GAElG,EAAA,GANe,OAAO,KAO1B;AAAA,SACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAET;AAER;"}
|
|
1
|
+
{"version":3,"file":"SelectInput.js","sources":["../../src/inputs/SelectInput.jsx"],"sourcesContent":["// Lista desplegable suelta, con la etiqueta arriba (mismo look que TextInput): la que pintan FormSelectField (formik) y la\n// de react-hook-form, y la que se usa sin formulario.\n//\n// Cada opción puede traer una explicación (description) que se lee en la lista para escoger; en la caja queda solo el\n// nombre de lo elegido. OJO: sin renderValue, MUI copiaba la opción completa a la caja y se leía «MenudeoCompra por pieza…».\n//\n// Con `multiple` se eligen varias: la lista se queda abierta, cada elegida lleva su palomita y en la caja quedan sus nombres\n// separados por coma, en el orden de las opciones. Al formulario llega la lista de valores (Yup.array().min(1) la valida).\n// Para catálogos largos, CatalogInput con `multiple` (busca y deja chips). Con varias, además: selectAll («Elegir todos»\n// arriba, con su casilla a medias si hay algunos) y checkboxes (una casilla en cada opción en lugar de la palomita).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSelectInput con defaultProps y\n// styleOverrides de sus partes: option, optionText.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Checkbox from '@mui/material/Checkbox';\nimport MenuItem from '@mui/material/MenuItem';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {selectAllStateOf} from '@/inputs/selectAllState.js';\nimport {piecePart, usePieceProps} 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.CalcitaSelectInput)\nconst THEME_NAME = 'CalcitaSelectInput';\nconst SelectOption = piecePart(THEME_NAME, 'Option', MenuItem);\nconst SelectOptionText = piecePart(THEME_NAME, 'OptionText', Box);\n// la opción de «Elegir todos»: no es de la lista y nunca llega al formulario\nconst SELECT_ALL_VALUE = '__calcita_select_all__';\n// con casillas, la palomita que el tema le pone al elegido sobra\nconst WITHOUT_CHECKMARK = Object.freeze({'&.Mui-selected::after': {content: 'none'}});\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|Array<string|number>|null|undefined} props.value '' = sin elegir; con multiple, la lista ([] = ninguna)\n * @param {(value: any) => void} props.onChange recibe el valor elegido (con multiple, la lista), no el evento\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string, disabled?: boolean}>} props.options\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] texto de la opción vacía\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias opciones\n * @param {boolean} [props.selectAll] con multiple: «Elegir todos» arriba de la lista\n * @param {boolean} [props.checkboxes] con multiple: una casilla en cada opción\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SelectInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSelectInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, options, error, helperText = ' ', placeholder, disabled = false, inputRef, multiple = false,\n selectAll = false, checkboxes = false, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'select'}-${generatedId}`;\n // OJO: la lista desplegable de MUI es un div y no un <input>: el `for` de la etiqueta no la une. Se nombra con su etiqueta\n // por aria-labelledby (labelId); sin esto el lector de pantalla no decía «Ciudad», solo lo elegido\n const labelId = `${inputId}-label`;\n const errorText = translateValidationMessage(t, error);\n const placeholderText = (\n <Typography component=\"span\"\n // OJO: el placeholder de un select activo se lee (text.secondary); text.disabled solo con el campo inactivo\n color={disabled ? 'text.disabled' : 'text.secondary'}>{placeholder ?? t('form.selectPlaceholder')}</Typography>\n );\n const renderSelectedValue = (selectedValue) => {\n if (multiple) {\n const selectedLabels = options.filter((option) => selectedValue.includes(option.value)).map((option) => option.label);\n return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholderText;\n }\n return options.find((option) => option.value === selectedValue)?.label ?? placeholderText;\n };\n const currentValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? '');\n const selectAllState = selectAllStateOf(currentValue, options.filter((option) => !option.disabled).map((option) => option.value));\n // OJO: con multiple, el autollenado del navegador puede mandar el valor como texto separado por comas (lo dice MUI)\n const handleChange = (event) => {\n const nextValue = event.target.value;\n const values = multiple && typeof nextValue === 'string' ? nextValue.split(',') : nextValue;\n // «Elegir todos» llega como una opción más: se cambia por todos (o por ninguno, si ya estaban todos)\n if (multiple && values.includes(SELECT_ALL_VALUE)) {\n onChange(selectAllState.toggled());\n return;\n }\n onChange(values);\n };\n\n return (\n <FieldFrame inputId={inputId} labelId={labelId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n select\n size=\"small\"\n fullWidth\n disabled={disabled}\n value={currentValue}\n onChange={handleChange}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n slotProps={{select: {multiple, displayEmpty: true, renderValue: renderSelectedValue, labelId}}}>\n {/* la opción vacía solo con una: con varias, se quitan picándolas otra vez */}\n {!multiple && <MenuItem value=\"\" disabled>{placeholderText}</MenuItem>}\n {multiple && selectAll && (\n <SelectOption value={SELECT_ALL_VALUE} ownSx={{fontWeight: 600, ...WITHOUT_CHECKMARK}}>\n <Checkbox size=\"small\" tabIndex={-1} disableRipple sx={{p: 0, mr: 1}}\n checked={selectAllState.allChosen} indeterminate={selectAllState.someChosen} />\n {selectAllState.allChosen ? t('form.unselectAll') : t('form.selectAll', {count: options.length})}\n </SelectOption>\n )}\n {options.map((option) => (\n // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del\n // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)\n <SelectOption key={option.value} value={option.value} disabled={option.disabled}\n ownSx={{whiteSpace: 'normal', ...(multiple && checkboxes && WITHOUT_CHECKMARK)}}>\n {multiple && checkboxes && (\n <Checkbox size=\"small\" tabIndex={-1} disableRipple sx={{p: 0, mr: 1}} checked={currentValue.includes(option.value)} />\n )}\n <SelectOptionText component=\"span\" ownSx={{display: 'flex', flexDirection: 'column', minWidth: 0, flexGrow: 1}}>\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n {option.description && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n )}\n </SelectOptionText>\n </SelectOption>\n ))}\n </TextField>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useId, useState } from "react";
|
|
4
|
+
import Button from "@mui/material/Button";
|
|
5
|
+
import Checkbox from "@mui/material/Checkbox";
|
|
6
|
+
import FormHelperText from "@mui/material/FormHelperText";
|
|
7
|
+
import List from "@mui/material/List";
|
|
8
|
+
import ListItemButton from "@mui/material/ListItemButton";
|
|
9
|
+
import ListItemIcon from "@mui/material/ListItemIcon";
|
|
10
|
+
import ListItemText from "@mui/material/ListItemText";
|
|
11
|
+
import Paper from "@mui/material/Paper";
|
|
12
|
+
import Stack from "@mui/material/Stack";
|
|
13
|
+
import TextField from "@mui/material/TextField";
|
|
14
|
+
import Tooltip from "@mui/material/Tooltip";
|
|
15
|
+
import Typography from "@mui/material/Typography";
|
|
16
|
+
import ChevronLeftRoundedIcon from "@mui/icons-material/ChevronLeftRounded";
|
|
17
|
+
import ChevronRightRoundedIcon from "@mui/icons-material/ChevronRightRounded";
|
|
18
|
+
import KeyboardDoubleArrowLeftRoundedIcon from "@mui/icons-material/KeyboardDoubleArrowLeftRounded";
|
|
19
|
+
import KeyboardDoubleArrowRightRoundedIcon from "@mui/icons-material/KeyboardDoubleArrowRightRounded";
|
|
20
|
+
import { translateValidationMessage } from "../forms/validationMessages.js";
|
|
21
|
+
import FieldFrame from "./FieldFrame.js";
|
|
22
|
+
import { selectAllStateOf } from "./selectAllState.js";
|
|
23
|
+
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
24
|
+
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
25
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
26
|
+
const DEFAULT_LIST_HEIGHT = 280;
|
|
27
|
+
const PHONE_ROTATION = Object.freeze({ transform: { xs: "rotate(90deg)", md: "none" } });
|
|
28
|
+
const THEME_NAME = "CalcitaTransferListInput";
|
|
29
|
+
const TransferRoot = piecePart(THEME_NAME, "Root", Stack);
|
|
30
|
+
const TransferList = piecePart(THEME_NAME, "List", Paper);
|
|
31
|
+
const TransferActions = piecePart(THEME_NAME, "Actions", Stack);
|
|
32
|
+
function plainTextOf(text) {
|
|
33
|
+
return String(text ?? "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().trim();
|
|
34
|
+
}
|
|
35
|
+
function TransferColumn({ title, items, markedValues, onMark, onMarkMany, onMoveOne, searchable, searchLabel, emptyText, listHeight, disabled, t }) {
|
|
36
|
+
const titleId = useId();
|
|
37
|
+
const [searchText, setSearchText] = useState("");
|
|
38
|
+
const wantedText = plainTextOf(searchText);
|
|
39
|
+
const visibleItems = wantedText ? items.filter((item) => plainTextOf(`${item.label} ${item.description ?? ""}`).includes(wantedText)) : items;
|
|
40
|
+
const visibleValues = visibleItems.filter((item) => !item.disabled).map((item) => item.value);
|
|
41
|
+
const markState = selectAllStateOf(markedValues, visibleValues);
|
|
42
|
+
return /* @__PURE__ */ jsxs(TransferList, { variant: "outlined", ownSx: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column" }, children: [
|
|
43
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "row", sx: { alignItems: "center", gap: 1, px: 1.5, py: 1, borderBottom: 1, borderColor: "divider" }, children: [
|
|
44
|
+
/* @__PURE__ */ jsx(
|
|
45
|
+
Checkbox,
|
|
46
|
+
{
|
|
47
|
+
size: "small",
|
|
48
|
+
sx: { p: 0.5 },
|
|
49
|
+
disabled: disabled || visibleValues.length === 0,
|
|
50
|
+
checked: markState.allChosen,
|
|
51
|
+
indeterminate: markState.someChosen,
|
|
52
|
+
onChange: () => onMarkMany(markState.toggled()),
|
|
53
|
+
slotProps: { input: { "aria-label": t("transfer.markVisible", { list: title }) } }
|
|
54
|
+
}
|
|
55
|
+
),
|
|
56
|
+
/* @__PURE__ */ jsx(Typography, { id: titleId, variant: "subtitle2", sx: { flexGrow: 1 }, children: title }),
|
|
57
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: markedValues.length > 0 ? t("transfer.markedOf", { marked: markedValues.length, count: items.length }) : items.length })
|
|
58
|
+
] }),
|
|
59
|
+
searchable && /* @__PURE__ */ jsx(
|
|
60
|
+
TextField,
|
|
61
|
+
{
|
|
62
|
+
size: "small",
|
|
63
|
+
type: "search",
|
|
64
|
+
value: searchText,
|
|
65
|
+
onChange: (event) => setSearchText(event.target.value),
|
|
66
|
+
placeholder: searchLabel,
|
|
67
|
+
disabled,
|
|
68
|
+
sx: { m: 1 },
|
|
69
|
+
slotProps: { htmlInput: { "aria-label": `${searchLabel} · ${title}` } }
|
|
70
|
+
}
|
|
71
|
+
),
|
|
72
|
+
/* @__PURE__ */ jsxs(
|
|
73
|
+
List,
|
|
74
|
+
{
|
|
75
|
+
dense: true,
|
|
76
|
+
role: "listbox",
|
|
77
|
+
"aria-labelledby": titleId,
|
|
78
|
+
"aria-multiselectable": "true",
|
|
79
|
+
sx: { height: listHeight, overflowY: "auto", py: 0 },
|
|
80
|
+
children: [
|
|
81
|
+
visibleItems.length === 0 && // vacía de verdad o vacía por lo buscado: no es lo mismo y se dice distinto
|
|
82
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { px: 2, py: 1.5 }, children: items.length > 0 ? t("transfer.noMatches", { text: searchText.trim() }) : emptyText }),
|
|
83
|
+
visibleItems.map((item) => {
|
|
84
|
+
const isMarked = markedValues.includes(item.value);
|
|
85
|
+
return /* @__PURE__ */ jsxs(
|
|
86
|
+
ListItemButton,
|
|
87
|
+
{
|
|
88
|
+
role: "option",
|
|
89
|
+
"aria-selected": isMarked,
|
|
90
|
+
disabled: disabled || item.disabled,
|
|
91
|
+
onClick: () => onMark(item.value),
|
|
92
|
+
onDoubleClick: () => onMoveOne(item.value),
|
|
93
|
+
sx: { py: 0.25 },
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 32 }, children: /* @__PURE__ */ jsx(Checkbox, { size: "small", edge: "start", tabIndex: -1, disableRipple: true, checked: isMarked, sx: { p: 0.5 } }) }),
|
|
96
|
+
/* @__PURE__ */ jsx(ListItemText, { primary: item.label, secondary: item.description })
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
item.value
|
|
100
|
+
);
|
|
101
|
+
})
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
107
|
+
function TransferListInput(props) {
|
|
108
|
+
const {
|
|
109
|
+
name,
|
|
110
|
+
label,
|
|
111
|
+
value,
|
|
112
|
+
onChange,
|
|
113
|
+
onBlur,
|
|
114
|
+
options,
|
|
115
|
+
availableTitle,
|
|
116
|
+
chosenTitle,
|
|
117
|
+
searchable,
|
|
118
|
+
listHeight = DEFAULT_LIST_HEIGHT,
|
|
119
|
+
error,
|
|
120
|
+
helperText = " ",
|
|
121
|
+
disabled = false,
|
|
122
|
+
sx,
|
|
123
|
+
texts,
|
|
124
|
+
...unknownProps
|
|
125
|
+
} = usePieceProps(props, THEME_NAME);
|
|
126
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
127
|
+
const { t } = useToolkitTranslation("common", texts);
|
|
128
|
+
const generatedId = useId();
|
|
129
|
+
const inputId = `${name ?? "transfer"}-${generatedId}`;
|
|
130
|
+
const errorText = translateValidationMessage(t, error);
|
|
131
|
+
const chosenValues = Array.isArray(value) ? value : [];
|
|
132
|
+
const [markedAvailable, setMarkedAvailable] = useState([]);
|
|
133
|
+
const [markedChosen, setMarkedChosen] = useState([]);
|
|
134
|
+
const available = options.filter((option) => !chosenValues.includes(option.value));
|
|
135
|
+
const chosen = chosenValues.map((chosenValue) => options.find((option) => option.value === chosenValue)).filter(Boolean);
|
|
136
|
+
const isSearchable = searchable ?? options.length > 8;
|
|
137
|
+
const titles = { available: availableTitle ?? t("transfer.available"), chosen: chosenTitle ?? t("transfer.chosen") };
|
|
138
|
+
const change = (values) => {
|
|
139
|
+
onChange(values);
|
|
140
|
+
onBlur?.();
|
|
141
|
+
};
|
|
142
|
+
const toggleIn = (setMarked) => (itemValue) => setMarked((current) => current.includes(itemValue) ? current.filter((markedValue) => markedValue !== itemValue) : [...current, itemValue]);
|
|
143
|
+
const moveToChosen = (values) => {
|
|
144
|
+
change([...chosenValues, ...values.filter((itemValue) => !chosenValues.includes(itemValue))]);
|
|
145
|
+
setMarkedAvailable((current) => current.filter((markedValue) => !values.includes(markedValue)));
|
|
146
|
+
};
|
|
147
|
+
const moveToAvailable = (values) => {
|
|
148
|
+
change(chosenValues.filter((chosenValue) => !values.includes(chosenValue)));
|
|
149
|
+
setMarkedChosen((current) => current.filter((markedValue) => !values.includes(markedValue)));
|
|
150
|
+
};
|
|
151
|
+
const movableOf = (items) => items.filter((item) => !item.disabled).map((item) => item.value);
|
|
152
|
+
const actionButton = (titleKey, Icon, onClick, isDisabled) => /* @__PURE__ */ jsx(Tooltip, { title: t(titleKey), children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
153
|
+
Button,
|
|
154
|
+
{
|
|
155
|
+
variant: "outlined",
|
|
156
|
+
size: "small",
|
|
157
|
+
"aria-label": t(titleKey),
|
|
158
|
+
disabled: disabled || isDisabled,
|
|
159
|
+
onClick,
|
|
160
|
+
sx: { minWidth: 40, px: 1 },
|
|
161
|
+
children: /* @__PURE__ */ jsx(Icon, { fontSize: "small", sx: PHONE_ROTATION })
|
|
162
|
+
}
|
|
163
|
+
) }) });
|
|
164
|
+
const lists = /* @__PURE__ */ jsxs(TransferRoot, { direction: { xs: "column", md: "row" }, ownSx: { gap: 1.5, alignItems: "stretch", ...sx }, id: inputId, children: [
|
|
165
|
+
/* @__PURE__ */ jsx(
|
|
166
|
+
TransferColumn,
|
|
167
|
+
{
|
|
168
|
+
title: titles.available,
|
|
169
|
+
items: available,
|
|
170
|
+
markedValues: markedAvailable,
|
|
171
|
+
onMark: toggleIn(setMarkedAvailable),
|
|
172
|
+
onMarkMany: setMarkedAvailable,
|
|
173
|
+
onMoveOne: (itemValue) => moveToChosen([itemValue]),
|
|
174
|
+
searchable: isSearchable,
|
|
175
|
+
searchLabel: t("transfer.search"),
|
|
176
|
+
emptyText: t("transfer.emptyAvailable"),
|
|
177
|
+
listHeight,
|
|
178
|
+
disabled,
|
|
179
|
+
t
|
|
180
|
+
}
|
|
181
|
+
),
|
|
182
|
+
/* @__PURE__ */ jsxs(TransferActions, { direction: { xs: "row", md: "column" }, ownSx: { gap: 1, justifyContent: "center", alignItems: "center" }, children: [
|
|
183
|
+
actionButton("transfer.moveMarked", ChevronRightRoundedIcon, () => moveToChosen(markedAvailable), markedAvailable.length === 0),
|
|
184
|
+
actionButton("transfer.moveAll", KeyboardDoubleArrowRightRoundedIcon, () => moveToChosen(movableOf(available)), movableOf(available).length === 0),
|
|
185
|
+
actionButton("transfer.returnMarked", ChevronLeftRoundedIcon, () => moveToAvailable(markedChosen), markedChosen.length === 0),
|
|
186
|
+
actionButton("transfer.returnAll", KeyboardDoubleArrowLeftRoundedIcon, () => moveToAvailable(movableOf(chosen)), movableOf(chosen).length === 0)
|
|
187
|
+
] }),
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
TransferColumn,
|
|
190
|
+
{
|
|
191
|
+
title: titles.chosen,
|
|
192
|
+
items: chosen,
|
|
193
|
+
markedValues: markedChosen,
|
|
194
|
+
onMark: toggleIn(setMarkedChosen),
|
|
195
|
+
onMarkMany: setMarkedChosen,
|
|
196
|
+
onMoveOne: (itemValue) => moveToAvailable([itemValue]),
|
|
197
|
+
searchable: isSearchable,
|
|
198
|
+
searchLabel: t("transfer.search"),
|
|
199
|
+
emptyText: t("transfer.emptyChosen"),
|
|
200
|
+
listHeight,
|
|
201
|
+
disabled,
|
|
202
|
+
t
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
] });
|
|
206
|
+
const field = /* @__PURE__ */ jsxs("div", { children: [
|
|
207
|
+
lists,
|
|
208
|
+
/* @__PURE__ */ jsx(FormHelperText, { error: Boolean(errorText), role: errorText ? "alert" : void 0, children: errorText ?? helperText })
|
|
209
|
+
] });
|
|
210
|
+
return label ? /* @__PURE__ */ jsx(FieldFrame, { inputId, label, children: field }) : field;
|
|
211
|
+
}
|
|
212
|
+
export {
|
|
213
|
+
TransferListInput as default
|
|
214
|
+
};
|
|
215
|
+
//# sourceMappingURL=TransferListInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TransferListInput.js","sources":["../../src/inputs/TransferListInput.jsx"],"sourcesContent":["// La lista de transferencia: dos columnas, «Disponibles» y «Elegidos», y botones para pasar de una a otra. Para elegir\n// muchos de una lista larga viendo siempre qué quedó de cada lado (los usuarios que pueden ver un reporte, las sucursales donde\n// puede comprar un cliente). Al formulario llega la lista de los elegidos, en el orden en que se pasaron.\n//\n// Cada columna trae su título con cuántos tiene, su buscador (searchable) y su casilla de «todos los que se ven» (con algo\n// buscado, solo esos). Se marcan los que se quieren pasar (clic o espacio) y se pasan con ›; » pasa todos los de la\n// columna. Doble clic pasa uno directo. En el teléfono las columnas van una debajo de otra y los botones apuntan hacia\n// abajo y arriba. Va fuera de una FormSection: necesita el ancho completo.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTransferListInput con defaultProps y\n// styleOverrides de sus partes: root, list, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport Button from '@mui/material/Button';\nimport Checkbox from '@mui/material/Checkbox';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport ChevronLeftRoundedIcon from '@mui/icons-material/ChevronLeftRounded';\nimport ChevronRightRoundedIcon from '@mui/icons-material/ChevronRightRounded';\nimport KeyboardDoubleArrowLeftRoundedIcon from '@mui/icons-material/KeyboardDoubleArrowLeftRounded';\nimport KeyboardDoubleArrowRightRoundedIcon from '@mui/icons-material/KeyboardDoubleArrowRightRounded';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {selectAllStateOf} from '@/inputs/selectAllState.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// lo alto de cada columna antes de deslizar\nconst DEFAULT_LIST_HEIGHT = 280;\n// en el teléfono los botones apuntan hacia abajo (pasar) y hacia arriba (regresar)\nconst PHONE_ROTATION = Object.freeze({transform: {xs: 'rotate(90deg)', md: 'none'}});\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaTransferListInput)\nconst THEME_NAME = 'CalcitaTransferListInput';\nconst TransferRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst TransferList = piecePart(THEME_NAME, 'List', Paper);\nconst TransferActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * Un texto sin acentos ni mayúsculas, para que «queretaro» encuentre «Querétaro».\n *\n * @param {unknown} text\n * @returns {string}\n */\nfunction plainTextOf(text) {\n return String(text ?? '').normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase().trim();\n}\n\n/**\n * Una columna: su título con cuántos tiene, su buscador, su casilla de «todos los que se ven» y sus renglones.\n *\n * @param {object} props\n */\nfunction TransferColumn({title, items, markedValues, onMark, onMarkMany, onMoveOne, searchable, searchLabel, emptyText, listHeight, disabled, t}) {\n const titleId = useId();\n const [searchText, setSearchText] = useState('');\n const wantedText = plainTextOf(searchText);\n const visibleItems = wantedText\n ? items.filter((item) => plainTextOf(`${item.label} ${item.description ?? ''}`).includes(wantedText))\n : items;\n const visibleValues = visibleItems.filter((item) => !item.disabled).map((item) => item.value);\n const markState = selectAllStateOf(markedValues, visibleValues);\n\n return (\n <TransferList variant=\"outlined\" ownSx={{flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column'}}>\n <Stack direction=\"row\" sx={{alignItems: 'center', gap: 1, px: 1.5, py: 1, borderBottom: 1, borderColor: 'divider'}}>\n <Checkbox size=\"small\" sx={{p: 0.5}} disabled={disabled || visibleValues.length === 0}\n checked={markState.allChosen} indeterminate={markState.someChosen}\n onChange={() => onMarkMany(markState.toggled())}\n slotProps={{input: {'aria-label': t('transfer.markVisible', {list: title})}}} />\n <Typography id={titleId} variant=\"subtitle2\" sx={{flexGrow: 1}}>{title}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {markedValues.length > 0 ? t('transfer.markedOf', {marked: markedValues.length, count: items.length}) : items.length}\n </Typography>\n </Stack>\n {searchable && (\n <TextField size=\"small\" type=\"search\" value={searchText} onChange={(event) => setSearchText(event.target.value)}\n placeholder={searchLabel} disabled={disabled} sx={{m: 1}}\n slotProps={{htmlInput: {'aria-label': `${searchLabel} · ${title}`}}} />\n )}\n <List dense role=\"listbox\" aria-labelledby={titleId} aria-multiselectable=\"true\"\n sx={{height: listHeight, overflowY: 'auto', py: 0}}>\n {visibleItems.length === 0 && (\n // vacía de verdad o vacía por lo buscado: no es lo mismo y se dice distinto\n <Typography variant=\"body2\" color=\"text.secondary\" sx={{px: 2, py: 1.5}}>\n {items.length > 0 ? t('transfer.noMatches', {text: searchText.trim()}) : emptyText}\n </Typography>\n )}\n {visibleItems.map((item) => {\n const isMarked = markedValues.includes(item.value);\n return (\n <ListItemButton key={item.value} role=\"option\" aria-selected={isMarked} disabled={disabled || item.disabled}\n onClick={() => onMark(item.value)} onDoubleClick={() => onMoveOne(item.value)} sx={{py: 0.25}}>\n <ListItemIcon sx={{minWidth: 32}}>\n <Checkbox size=\"small\" edge=\"start\" tabIndex={-1} disableRipple checked={isMarked} sx={{p: 0.5}} />\n </ListItemIcon>\n <ListItemText primary={item.label} secondary={item.description} />\n </ListItemButton>\n );\n })}\n </List>\n </TransferList>\n );\n}\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} [props.label] ya traducida\n * @param {Array<string|number>|null|undefined} props.value los elegidos\n * @param {(values: Array<string|number>) => void} props.onChange\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string, disabled?: boolean}>} props.options todos\n * @param {string} [props.availableTitle] ya traducido; por omisión «Disponibles»\n * @param {string} [props.chosenTitle] ya traducido; por omisión «Elegidos»\n * @param {boolean} [props.searchable] un buscador en cada columna (por omisión, con más de 8 opciones)\n * @param {number} [props.listHeight] lo alto de cada columna, en px\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {boolean} [props.disabled]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function TransferListInput(props) {\n const {\n name, label, value, onChange, onBlur, options, availableTitle, chosenTitle, searchable, listHeight = DEFAULT_LIST_HEIGHT, error,\n helperText = ' ', disabled = false, sx, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'transfer'}-${generatedId}`;\n const errorText = translateValidationMessage(t, error);\n const chosenValues = Array.isArray(value) ? value : [];\n // lo marcado para pasar, de cada lado\n const [markedAvailable, setMarkedAvailable] = useState([]);\n const [markedChosen, setMarkedChosen] = useState([]);\n const available = options.filter((option) => !chosenValues.includes(option.value));\n // los elegidos en el orden en que se pasaron\n const chosen = chosenValues.map((chosenValue) => options.find((option) => option.value === chosenValue)).filter(Boolean);\n const isSearchable = searchable ?? options.length > 8;\n const titles = {available: availableTitle ?? t('transfer.available'), chosen: chosenTitle ?? t('transfer.chosen')};\n\n const change = (values) => {\n onChange(values);\n onBlur?.();\n };\n const toggleIn = (setMarked) => (itemValue) => setMarked((current) => (\n current.includes(itemValue) ? current.filter((markedValue) => markedValue !== itemValue) : [...current, itemValue]\n ));\n const moveToChosen = (values) => {\n change([...chosenValues, ...values.filter((itemValue) => !chosenValues.includes(itemValue))]);\n setMarkedAvailable((current) => current.filter((markedValue) => !values.includes(markedValue)));\n };\n const moveToAvailable = (values) => {\n change(chosenValues.filter((chosenValue) => !values.includes(chosenValue)));\n setMarkedChosen((current) => current.filter((markedValue) => !values.includes(markedValue)));\n };\n const movableOf = (items) => items.filter((item) => !item.disabled).map((item) => item.value);\n const actionButton = (titleKey, Icon, onClick, isDisabled) => (\n <Tooltip title={t(titleKey)}>\n <span>\n <Button variant=\"outlined\" size=\"small\" aria-label={t(titleKey)} disabled={disabled || isDisabled} onClick={onClick}\n sx={{minWidth: 40, px: 1}}>\n <Icon fontSize=\"small\" sx={PHONE_ROTATION} />\n </Button>\n </span>\n </Tooltip>\n );\n\n const lists = (\n <TransferRoot direction={{xs: 'column', md: 'row'}} ownSx={{gap: 1.5, alignItems: 'stretch', ...sx}} id={inputId}>\n <TransferColumn title={titles.available} items={available} markedValues={markedAvailable}\n onMark={toggleIn(setMarkedAvailable)} onMarkMany={setMarkedAvailable} onMoveOne={(itemValue) => moveToChosen([itemValue])}\n searchable={isSearchable} searchLabel={t('transfer.search')} emptyText={t('transfer.emptyAvailable')}\n listHeight={listHeight} disabled={disabled} t={t} />\n <TransferActions direction={{xs: 'row', md: 'column'}} ownSx={{gap: 1, justifyContent: 'center', alignItems: 'center'}}>\n {actionButton('transfer.moveMarked', ChevronRightRoundedIcon, () => moveToChosen(markedAvailable), markedAvailable.length === 0)}\n {actionButton('transfer.moveAll', KeyboardDoubleArrowRightRoundedIcon, () => moveToChosen(movableOf(available)), movableOf(available).length === 0)}\n {actionButton('transfer.returnMarked', ChevronLeftRoundedIcon, () => moveToAvailable(markedChosen), markedChosen.length === 0)}\n {actionButton('transfer.returnAll', KeyboardDoubleArrowLeftRoundedIcon, () => moveToAvailable(movableOf(chosen)), movableOf(chosen).length === 0)}\n </TransferActions>\n <TransferColumn title={titles.chosen} items={chosen} markedValues={markedChosen}\n onMark={toggleIn(setMarkedChosen)} onMarkMany={setMarkedChosen} onMoveOne={(itemValue) => moveToAvailable([itemValue])}\n searchable={isSearchable} searchLabel={t('transfer.search')} emptyText={t('transfer.emptyChosen')}\n listHeight={listHeight} disabled={disabled} t={t} />\n </TransferRoot>\n );\n\n const field = (\n <div>\n {lists}\n <FormHelperText error={Boolean(errorText)} role={errorText ? 'alert' : undefined}>{errorText ?? helperText}</FormHelperText>\n </div>\n );\n // sin etiqueta va solo (la pantalla pone el título a su manera)\n return label ? <FieldFrame inputId={inputId} label={label}>{field}</FieldFrame> : field;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const INPUT_MASK = Object.freeze({
|
|
2
|
+
// 10 dígitos en grupos de 3, 3 y 4; si su país lo escribe distinto, pase su propio patrón
|
|
3
|
+
phone: "999 999 9999",
|
|
4
|
+
postalCode: "99999",
|
|
5
|
+
// la CLABE: banco (3), plaza (3), cuenta (11) y dígito verificador (1)
|
|
6
|
+
clabe: "999 999 99999999999 9",
|
|
7
|
+
card: "9999 9999 9999 9999",
|
|
8
|
+
// RFC de persona física (13); el de persona moral (12) cabe igual
|
|
9
|
+
rfc: "XXXXXXXXXXXXX",
|
|
10
|
+
curp: "XXXXXXXXXXXXXXXXXX",
|
|
11
|
+
// el folio fiscal (UUID) de una factura
|
|
12
|
+
uuid: "HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH"
|
|
13
|
+
});
|
|
14
|
+
const SLOT_RULES = Object.freeze({
|
|
15
|
+
9: /^\d$/,
|
|
16
|
+
A: /^[A-ZÑ]$/,
|
|
17
|
+
X: /^[A-Z0-9Ñ&]$/,
|
|
18
|
+
H: /^[0-9A-F]$/
|
|
19
|
+
});
|
|
20
|
+
function maskPatternOf(mask) {
|
|
21
|
+
return INPUT_MASK[mask] ?? mask;
|
|
22
|
+
}
|
|
23
|
+
function isSlot(patternCharacter) {
|
|
24
|
+
return patternCharacter in SLOT_RULES;
|
|
25
|
+
}
|
|
26
|
+
function slotsOf(pattern) {
|
|
27
|
+
return [...pattern].filter(isSlot);
|
|
28
|
+
}
|
|
29
|
+
function rawValueOf(typedText, pattern) {
|
|
30
|
+
const slots = slotsOf(pattern);
|
|
31
|
+
let raw = "";
|
|
32
|
+
for (const character of (typedText ?? "").toLocaleUpperCase("es-MX")) {
|
|
33
|
+
if (raw.length === slots.length) {
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (SLOT_RULES[slots[raw.length]].test(character)) {
|
|
37
|
+
raw += character;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return raw;
|
|
41
|
+
}
|
|
42
|
+
function maskedTextOf(rawValue, pattern) {
|
|
43
|
+
const raw = rawValueOf(rawValue ?? "", pattern);
|
|
44
|
+
let shown = "";
|
|
45
|
+
let usedCharacters = 0;
|
|
46
|
+
for (const patternCharacter of pattern) {
|
|
47
|
+
if (usedCharacters === raw.length) {
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
if (isSlot(patternCharacter)) {
|
|
51
|
+
shown += raw[usedCharacters];
|
|
52
|
+
usedCharacters += 1;
|
|
53
|
+
} else {
|
|
54
|
+
shown += patternCharacter;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return shown;
|
|
58
|
+
}
|
|
59
|
+
function caretPositionOf(typedText, caretPosition, pattern) {
|
|
60
|
+
const valueCharactersBefore = rawValueOf(typedText.slice(0, caretPosition), pattern).length;
|
|
61
|
+
const shown = maskedTextOf(rawValueOf(typedText, pattern), pattern);
|
|
62
|
+
let seen = 0;
|
|
63
|
+
for (let position = 0; position < shown.length; position += 1) {
|
|
64
|
+
if (seen === valueCharactersBefore) {
|
|
65
|
+
return position;
|
|
66
|
+
}
|
|
67
|
+
if (isSlot(pattern[position])) {
|
|
68
|
+
seen += 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return shown.length;
|
|
72
|
+
}
|
|
73
|
+
function isMaskComplete(rawValue, pattern) {
|
|
74
|
+
return rawValueOf(rawValue ?? "", pattern).length === slotsOf(pattern).length;
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
INPUT_MASK,
|
|
78
|
+
caretPositionOf,
|
|
79
|
+
isMaskComplete,
|
|
80
|
+
maskPatternOf,
|
|
81
|
+
maskedTextOf,
|
|
82
|
+
rawValueOf
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=inputMasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inputMasks.js","sources":["../../src/inputs/inputMasks.js"],"sourcesContent":["// Las máscaras de los campos que se escriben con forma fija: un teléfono, una CLABE, un RFC, un folio fiscal. MaskedInput\n// las usa para que la persona vea los separadores mientras escribe y al formulario llegue el valor LIMPIO (solo dígitos o\n// letras, en mayúsculas), que es lo que valida y guarda el servidor.\n//\n// Un patrón es un texto donde cada lugar dice qué cabe; lo demás son separadores que pone la máscara:\n// 9 un dígito\n// A una letra (Ñ incluida), en mayúscula\n// X una letra o un dígito, en mayúscula (también & y Ñ, que llevan los RFC)\n// H un dígito hexadecimal 0-9 A-F, en mayúscula (los folios fiscales)\n// Por ejemplo '999 999 9999' muestra «555 123 4567» y al formulario llega «5551234567».\n//\n// Lo que no cabe en el lugar que sigue se ignora (una letra donde va un dígito) y nada pasa del largo del patrón. El RFC y la\n// CURP solo se filtran y se pasan a mayúsculas mientras se escriben: que estén bien formados lo revisan sus reglas.\n//\n// Puro (sin React): se prueba con node (inputMasks.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n/** Los patrones de siempre, para no escribirlos en cada pantalla. Uno propio se pasa como texto. */\nexport const INPUT_MASK = Object.freeze({\n // 10 dígitos en grupos de 3, 3 y 4; si su país lo escribe distinto, pase su propio patrón\n phone: '999 999 9999',\n postalCode: '99999',\n // la CLABE: banco (3), plaza (3), cuenta (11) y dígito verificador (1)\n clabe: '999 999 99999999999 9',\n card: '9999 9999 9999 9999',\n // RFC de persona física (13); el de persona moral (12) cabe igual\n rfc: 'XXXXXXXXXXXXX',\n curp: 'XXXXXXXXXXXXXXXXXX',\n // el folio fiscal (UUID) de una factura\n uuid: 'HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH',\n});\n\n// qué cabe en cada clase de lugar del patrón\nconst SLOT_RULES = Object.freeze({\n 9: /^\\d$/,\n A: /^[A-ZÑ]$/,\n X: /^[A-Z0-9Ñ&]$/,\n H: /^[0-9A-F]$/,\n});\n\n/**\n * El patrón de una máscara: el nombre de una de INPUT_MASK o el patrón mismo.\n *\n * @param {string} mask 'phone' | '999-999' …\n * @returns {string}\n */\nexport function maskPatternOf(mask) {\n return INPUT_MASK[mask] ?? mask;\n}\n\n/**\n * @param {string} patternCharacter\n * @returns {boolean}\n */\nfunction isSlot(patternCharacter) {\n return patternCharacter in SLOT_RULES;\n}\n\n/**\n * Los lugares del patrón, en orden (sin los separadores).\n *\n * @param {string} pattern\n * @returns {string[]}\n */\nfunction slotsOf(pattern) {\n return [...pattern].filter(isSlot);\n}\n\n/**\n * El valor limpio de lo que se escribió (o se pegó): cada carácter va al lugar que sigue si cabe; lo que no, se ignora.\n *\n * @param {string} typedText lo que hay en el campo, con o sin separadores\n * @param {string} pattern\n * @returns {string} solo los dígitos o letras, en mayúsculas, hasta el largo del patrón\n */\nexport function rawValueOf(typedText, pattern) {\n const slots = slotsOf(pattern);\n let raw = '';\n for (const character of (typedText ?? '').toLocaleUpperCase('es-MX')) {\n if (raw.length === slots.length) {\n break;\n }\n if (SLOT_RULES[slots[raw.length]].test(character)) {\n raw += character;\n }\n }\n return raw;\n}\n\n/**\n * El valor limpio con sus separadores, como se ve en el campo. Los separadores solo salen entre lo que ya se escribió (sin\n * guiones de más al final).\n *\n * @param {string|null|undefined} rawValue\n * @param {string} pattern\n * @returns {string}\n */\nexport function maskedTextOf(rawValue, pattern) {\n const raw = rawValueOf(rawValue ?? '', pattern);\n let shown = '';\n let usedCharacters = 0;\n for (const patternCharacter of pattern) {\n if (usedCharacters === raw.length) {\n break;\n }\n if (isSlot(patternCharacter)) {\n shown += raw[usedCharacters];\n usedCharacters += 1;\n } else {\n shown += patternCharacter;\n }\n }\n return shown;\n}\n\n/**\n * Dónde queda el cursor después de poner los separadores: justo después del mismo número de dígitos o letras que tenía\n * antes. Sin esto, escribir en medio de un teléfono mandaría el cursor al final.\n *\n * @param {string} typedText lo que había en el campo al escribir\n * @param {number} caretPosition dónde estaba el cursor en typedText\n * @param {string} pattern\n * @returns {number} la posición en el texto ya con sus separadores\n */\nexport function caretPositionOf(typedText, caretPosition, pattern) {\n const valueCharactersBefore = rawValueOf(typedText.slice(0, caretPosition), pattern).length;\n const shown = maskedTextOf(rawValueOf(typedText, pattern), pattern);\n let seen = 0;\n for (let position = 0; position < shown.length; position += 1) {\n if (seen === valueCharactersBefore) {\n return position;\n }\n if (isSlot(pattern[position])) {\n seen += 1;\n }\n }\n return shown.length;\n}\n\n/**\n * ¿Ya se llenaron todos los lugares?\n *\n * @param {string|null|undefined} rawValue\n * @param {string} pattern\n * @returns {boolean}\n */\nexport function isMaskComplete(rawValue, pattern) {\n return rawValueOf(rawValue ?? '', pattern).length === slotsOf(pattern).length;\n}\n"],"names":[],"mappings":"AAmBY,MAAC,aAAa,OAAO,OAAO;AAAA;AAAA,EAEpC,OAAO;AAAA,EACP,YAAY;AAAA;AAAA,EAEZ,OAAO;AAAA,EACP,MAAM;AAAA;AAAA,EAEN,KAAK;AAAA,EACL,MAAM;AAAA;AAAA,EAEN,MAAM;AACV,CAAC;AAGD,MAAM,aAAa,OAAO,OAAO;AAAA,EAC7B,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACP,CAAC;AAQM,SAAS,cAAc,MAAM;AAChC,SAAO,WAAW,IAAI,KAAK;AAC/B;AAMA,SAAS,OAAO,kBAAkB;AAC9B,SAAO,oBAAoB;AAC/B;AAQA,SAAS,QAAQ,SAAS;AACtB,SAAO,CAAC,GAAG,OAAO,EAAE,OAAO,MAAM;AACrC;AASO,SAAS,WAAW,WAAW,SAAS;AAC3C,QAAM,QAAQ,QAAQ,OAAO;AAC7B,MAAI,MAAM;AACV,aAAW,cAAc,aAAa,IAAI,kBAAkB,OAAO,GAAG;AAClE,QAAI,IAAI,WAAW,MAAM,QAAQ;AAC7B;AAAA,IACJ;AACA,QAAI,WAAW,MAAM,IAAI,MAAM,CAAC,EAAE,KAAK,SAAS,GAAG;AAC/C,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAUO,SAAS,aAAa,UAAU,SAAS;AAC5C,QAAM,MAAM,WAAW,YAAY,IAAI,OAAO;AAC9C,MAAI,QAAQ;AACZ,MAAI,iBAAiB;AACrB,aAAW,oBAAoB,SAAS;AACpC,QAAI,mBAAmB,IAAI,QAAQ;AAC/B;AAAA,IACJ;AACA,QAAI,OAAO,gBAAgB,GAAG;AAC1B,eAAS,IAAI,cAAc;AAC3B,wBAAkB;AAAA,IACtB,OAAO;AACH,eAAS;AAAA,IACb;AAAA,EACJ;AACA,SAAO;AACX;AAWO,SAAS,gBAAgB,WAAW,eAAe,SAAS;AAC/D,QAAM,wBAAwB,WAAW,UAAU,MAAM,GAAG,aAAa,GAAG,OAAO,EAAE;AACrF,QAAM,QAAQ,aAAa,WAAW,WAAW,OAAO,GAAG,OAAO;AAClE,MAAI,OAAO;AACX,WAAS,WAAW,GAAG,WAAW,MAAM,QAAQ,YAAY,GAAG;AAC3D,QAAI,SAAS,uBAAuB;AAChC,aAAO;AAAA,IACX;AACA,QAAI,OAAO,QAAQ,QAAQ,CAAC,GAAG;AAC3B,cAAQ;AAAA,IACZ;AAAA,EACJ;AACA,SAAO,MAAM;AACjB;AASO,SAAS,eAAe,UAAU,SAAS;AAC9C,SAAO,WAAW,YAAY,IAAI,OAAO,EAAE,WAAW,QAAQ,OAAO,EAAE;AAC3E;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function selectAllStateOf(selectedValues, candidateValues) {
|
|
2
|
+
const selected = new Set(selectedValues);
|
|
3
|
+
const chosenCount = candidateValues.filter((value) => selected.has(value)).length;
|
|
4
|
+
const allChosen = candidateValues.length > 0 && chosenCount === candidateValues.length;
|
|
5
|
+
return {
|
|
6
|
+
allChosen,
|
|
7
|
+
someChosen: chosenCount > 0 && !allChosen,
|
|
8
|
+
toggled: () => {
|
|
9
|
+
if (allChosen) {
|
|
10
|
+
const candidates = new Set(candidateValues);
|
|
11
|
+
return selectedValues.filter((value) => !candidates.has(value));
|
|
12
|
+
}
|
|
13
|
+
return [...selectedValues, ...candidateValues.filter((value) => !selected.has(value))];
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
selectAllStateOf
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=selectAllState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectAllState.js","sources":["../../src/inputs/selectAllState.js"],"sourcesContent":["// «Elegir todos» de una lista de varias opciones: si su casilla va marcada, a medias o vacía, y qué queda al picarla.\n// Lo comparten la lista desplegable, el catálogo con buscador y el grupo de casillas.\n//\n// «Todos» son los candidatos: todas las opciones, o las que coinciden con lo buscado. Picarlo con todos ya elegidos los\n// quita; si no, agrega los que faltan al final (lo que ya estaba elegido se queda en su orden).\n//\n// Puro (sin React): se prueba con node (selectAllState.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n/**\n * @template T\n * @param {T[]} selectedValues lo elegido hoy\n * @param {T[]} candidateValues lo que abarca «todos» (sin las opciones apagadas)\n * @returns {{allChosen: boolean, someChosen: boolean, toggled: () => T[]}} someChosen: algunos sí y otros no (casilla a medias)\n */\nexport function selectAllStateOf(selectedValues, candidateValues) {\n const selected = new Set(selectedValues);\n const chosenCount = candidateValues.filter((value) => selected.has(value)).length;\n const allChosen = candidateValues.length > 0 && chosenCount === candidateValues.length;\n return {\n allChosen,\n someChosen: chosenCount > 0 && !allChosen,\n toggled: () => {\n if (allChosen) {\n const candidates = new Set(candidateValues);\n return selectedValues.filter((value) => !candidates.has(value));\n }\n return [...selectedValues, ...candidateValues.filter((value) => !selected.has(value))];\n },\n };\n}\n"],"names":[],"mappings":"AAgBO,SAAS,iBAAiB,gBAAgB,iBAAiB;AAC9D,QAAM,WAAW,IAAI,IAAI,cAAc;AACvC,QAAM,cAAc,gBAAgB,OAAO,CAAC,UAAU,SAAS,IAAI,KAAK,CAAC,EAAE;AAC3E,QAAM,YAAY,gBAAgB,SAAS,KAAK,gBAAgB,gBAAgB;AAChF,SAAO;AAAA,IACH;AAAA,IACA,YAAY,cAAc,KAAK,CAAC;AAAA,IAChC,SAAS,MAAM;AACX,UAAI,WAAW;AACX,cAAM,aAAa,IAAI,IAAI,eAAe;AAC1C,eAAO,eAAe,OAAO,CAAC,UAAU,CAAC,WAAW,IAAI,KAAK,CAAC;AAAA,MAClE;AACA,aAAO,CAAC,GAAG,gBAAgB,GAAG,gBAAgB,OAAO,CAAC,UAAU,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC;AAAA,IACzF;AAAA,EACR;AACA;"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { newIdentifier } from "../identifiers.js";
|
|
4
|
+
function useRowKeys(rowCount) {
|
|
5
|
+
const keysRef = useRef([]);
|
|
6
|
+
if (keysRef.current.length !== rowCount) {
|
|
7
|
+
keysRef.current = Array.from({ length: rowCount }, (unused, index) => keysRef.current[index] ?? newIdentifier());
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
keys: keysRef.current,
|
|
11
|
+
added: () => {
|
|
12
|
+
keysRef.current = [...keysRef.current, newIdentifier()];
|
|
13
|
+
},
|
|
14
|
+
removed: (index) => {
|
|
15
|
+
keysRef.current = keysRef.current.filter((unused, keyIndex) => keyIndex !== index);
|
|
16
|
+
},
|
|
17
|
+
// un renglón limpio es otro renglón: los campos de adentro empiezan de cero
|
|
18
|
+
renewed: (index) => {
|
|
19
|
+
keysRef.current = keysRef.current.map((key, keyIndex) => keyIndex === index ? newIdentifier() : key);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
useRowKeys
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=useRowKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRowKeys.js","sources":["../../src/inputs/useRowKeys.js"],"sourcesContent":["// Una llave estable por renglón de una lista que se repite, para formik (react-hook-form ya trae la suya en useFieldArray).\n//\n// Por qué no el índice: al quitar el renglón 2, el 3 pasa a ser el 2; con el índice como llave React le dejaría al nuevo\n// 2 lo que tenía el viejo (lo escrito a medias en una lista de opciones, un error), y se vería el dato de otro renglón.\n//\n// Si la lista cambia desde afuera (el formulario se limpia o llega del servidor), las llaves se vuelven a hacer.\n//\n// Hook genérico: function (regla de la casa).\nimport {useRef} from 'react';\n\nimport {newIdentifier} from '@/identifiers.js';\n\n/**\n * @param {number} rowCount cuántos renglones tiene la lista hoy\n * @returns {{keys: string[], added: () => void, removed: (index: number) => void, renewed: (index: number) => void}}\n * added / removed / renewed se llaman JUNTO con el cambio de la lista (agregar al final, quitar uno, limpiar uno)\n */\nexport function useRowKeys(rowCount) {\n const keysRef = useRef([]);\n // la lista cambió desde afuera: tantas llaves como renglones\n if (keysRef.current.length !== rowCount) {\n keysRef.current = Array.from({length: rowCount}, (unused, index) => keysRef.current[index] ?? newIdentifier());\n }\n return {\n keys: keysRef.current,\n added: () => {\n keysRef.current = [...keysRef.current, newIdentifier()];\n },\n removed: (index) => {\n keysRef.current = keysRef.current.filter((unused, keyIndex) => keyIndex !== index);\n },\n // un renglón limpio es otro renglón: los campos de adentro empiezan de cero\n renewed: (index) => {\n keysRef.current = keysRef.current.map((key, keyIndex) => (keyIndex === index ? newIdentifier() : key));\n },\n };\n}\n"],"names":[],"mappings":";;;AAiBO,SAAS,WAAW,UAAU;AACjC,QAAM,UAAU,OAAO,EAAE;AAEzB,MAAI,QAAQ,QAAQ,WAAW,UAAU;AACrC,YAAQ,UAAU,MAAM,KAAK,EAAC,QAAQ,SAAQ,GAAG,CAAC,QAAQ,UAAU,QAAQ,QAAQ,KAAK,KAAK,cAAa,CAAE;AAAA,EACjH;AACA,SAAO;AAAA,IACH,MAAM,QAAQ;AAAA,IACd,OAAO,MAAM;AACT,cAAQ,UAAU,CAAC,GAAG,QAAQ,SAAS,cAAa,CAAE;AAAA,IAC1D;AAAA,IACA,SAAS,CAAC,UAAU;AAChB,cAAQ,UAAU,QAAQ,QAAQ,OAAO,CAAC,QAAQ,aAAa,aAAa,KAAK;AAAA,IACrF;AAAA;AAAA,IAEA,SAAS,CAAC,UAAU;AAChB,cAAQ,UAAU,QAAQ,QAAQ,IAAI,CAAC,KAAK,aAAc,aAAa,QAAQ,cAAa,IAAK,GAAI;AAAA,IACzG;AAAA,EACR;AACA;"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import Button from "@mui/material/Button";
|
|
4
|
+
import Link from "@mui/material/Link";
|
|
5
|
+
import Paper from "@mui/material/Paper";
|
|
6
|
+
import Stack from "@mui/material/Stack";
|
|
7
|
+
import Typography from "@mui/material/Typography";
|
|
8
|
+
import { FLOATING_BOTTOM_SPACE_CSS_VARIABLE } from "../appShell/shellSizes.js";
|
|
9
|
+
import { DEFAULT_COOKIE_CONSENT_KEY, useCookieConsent } from "./useCookieConsent.js";
|
|
10
|
+
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
11
|
+
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
12
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
13
|
+
const THEME_NAME = "CalcitaCookieNotice";
|
|
14
|
+
const NoticeRoot = piecePart(THEME_NAME, "Root", Paper);
|
|
15
|
+
const NoticeText = piecePart(THEME_NAME, "Text", Typography);
|
|
16
|
+
const NoticeActions = piecePart(THEME_NAME, "Actions", Stack);
|
|
17
|
+
const NOTICE_MAX_WIDTH = 420;
|
|
18
|
+
const NOTICE_Z_INDEX = 1200;
|
|
19
|
+
function CookieNotice(props) {
|
|
20
|
+
const {
|
|
21
|
+
version = "1",
|
|
22
|
+
storageKey = DEFAULT_COOKIE_CONSENT_KEY,
|
|
23
|
+
text,
|
|
24
|
+
policyHref,
|
|
25
|
+
onPolicyClick,
|
|
26
|
+
policyLabel,
|
|
27
|
+
acceptLabel,
|
|
28
|
+
onAccept,
|
|
29
|
+
actions,
|
|
30
|
+
sx,
|
|
31
|
+
texts,
|
|
32
|
+
...unknownProps
|
|
33
|
+
} = usePieceProps(props, THEME_NAME);
|
|
34
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
35
|
+
const { t } = useToolkitTranslation("common", texts);
|
|
36
|
+
const { hasAccepted, accept } = useCookieConsent({ version, storageKey });
|
|
37
|
+
if (hasAccepted) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const policyText = policyLabel ?? t("legal.cookies.policy");
|
|
41
|
+
const policyLink = onPolicyClick ? /* @__PURE__ */ jsx(Link, { component: "button", type: "button", onClick: onPolicyClick, sx: { verticalAlign: "baseline" }, children: policyText }) : policyHref && /* @__PURE__ */ jsx(Link, { href: policyHref, target: "_blank", rel: "noopener noreferrer", children: policyText });
|
|
42
|
+
return /* @__PURE__ */ jsxs(
|
|
43
|
+
NoticeRoot,
|
|
44
|
+
{
|
|
45
|
+
role: "region",
|
|
46
|
+
"aria-label": t("legal.cookies.region"),
|
|
47
|
+
elevation: 8,
|
|
48
|
+
ownSx: {
|
|
49
|
+
position: "fixed",
|
|
50
|
+
zIndex: NOTICE_Z_INDEX,
|
|
51
|
+
left: { xs: 16, sm: 24 },
|
|
52
|
+
right: { xs: 16, sm: "auto" },
|
|
53
|
+
maxWidth: { sm: NOTICE_MAX_WIDTH },
|
|
54
|
+
bottom: `calc(16px + env(safe-area-inset-bottom, 0px) + var(${FLOATING_BOTTOM_SPACE_CSS_VARIABLE}, 0px))`,
|
|
55
|
+
p: 2.5,
|
|
56
|
+
borderRadius: 3
|
|
57
|
+
},
|
|
58
|
+
sx,
|
|
59
|
+
children: [
|
|
60
|
+
/* @__PURE__ */ jsxs(NoticeText, { variant: "body2", ownSx: { mb: 2 }, children: [
|
|
61
|
+
text ?? t("legal.cookies.text"),
|
|
62
|
+
" ",
|
|
63
|
+
policyLink
|
|
64
|
+
] }),
|
|
65
|
+
/* @__PURE__ */ jsxs(NoticeActions, { direction: "row", ownSx: { gap: 1, justifyContent: "flex-end", flexWrap: "wrap" }, children: [
|
|
66
|
+
actions,
|
|
67
|
+
/* @__PURE__ */ jsx(Button, { variant: "contained", onClick: () => {
|
|
68
|
+
accept();
|
|
69
|
+
onAccept?.({ version, acceptedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
70
|
+
}, children: acceptLabel ?? t("legal.cookies.accept") })
|
|
71
|
+
] })
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
CookieNotice as default
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=CookieNotice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CookieNotice.js","sources":["../../src/legal/CookieNotice.jsx"],"sourcesContent":["// El aviso de cookies: un recuadro abajo con el texto, la liga a la política y «Aceptar». Sale hasta que la persona acepta\n// ESA versión del aviso en este navegador; al cambiar la versión vuelve a salir. No tapa la pantalla ni la bloquea: se puede\n// seguir trabajando.\n//\n// La política se abre con su dirección (policyHref, en otra pestaña) o con lo que diga la pantalla (onPolicyClick: un\n// PdfViewerDialog, por ejemplo). El texto, los botones y dónde se recuerda se cambian desde afuera; para uno a su manera está\n// useCookieConsent.\n//\n// Con la barra de pestañas abajo del teléfono, se sube para no quedar encima (FLOATING_BOTTOM_SPACE del marco).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaCookieNotice con defaultProps y\n// styleOverrides de sus partes: root, text, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport Link from '@mui/material/Link';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {FLOATING_BOTTOM_SPACE_CSS_VARIABLE} from '@/appShell/shellSizes.js';\nimport {DEFAULT_COOKIE_CONSENT_KEY, useCookieConsent} from '@/legal/useCookieConsent.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaCookieNotice)\nconst THEME_NAME = 'CalcitaCookieNotice';\nconst NoticeRoot = piecePart(THEME_NAME, 'Root', Paper);\nconst NoticeText = piecePart(THEME_NAME, 'Text', Typography);\nconst NoticeActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n// lo ancho del recuadro en escritorio: un párrafo cómodo, no toda la orilla\nconst NOTICE_MAX_WIDTH = 420;\n// por encima de la app, debajo de los diálogos (MUI: modal 1300)\nconst NOTICE_Z_INDEX = 1200;\n\n/**\n * @param {object} props\n * @param {string} [props.version] la del aviso; al cambiarla se vuelve a preguntar (por omisión '1')\n * @param {string} [props.storageKey] dónde se recuerda en este navegador (por omisión 'calcita:cookieConsent')\n * @param {import('react').ReactNode} [props.text] ya traducido; por omisión el del paquete\n * @param {string} [props.policyHref] la dirección de la política (se abre en otra pestaña)\n * @param {() => void} [props.onPolicyClick] en lugar de la dirección: la pantalla la abre a su manera\n * @param {string} [props.policyLabel] ya traducido; por omisión «Política de cookies»\n * @param {string} [props.acceptLabel] ya traducido; por omisión «Aceptar»\n * @param {(acceptance: {version: string, acceptedAt: string}) => void} [props.onAccept] al aceptar (para registrarlo en su API)\n * @param {import('react').ReactNode} [props.actions] botones suyos junto a «Aceptar» («Configurar»)\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function CookieNotice(props) {\n const {\n version = '1', storageKey = DEFAULT_COOKIE_CONSENT_KEY, text, policyHref, onPolicyClick, policyLabel, acceptLabel, onAccept, actions, sx,\n texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const {hasAccepted, accept} = useCookieConsent({version, storageKey});\n\n // guard clause: ya aceptó esta versión\n if (hasAccepted) {\n return null;\n }\n\n const policyText = policyLabel ?? t('legal.cookies.policy');\n const policyLink = onPolicyClick\n ? <Link component=\"button\" type=\"button\" onClick={onPolicyClick} sx={{verticalAlign: 'baseline'}}>{policyText}</Link>\n : policyHref && <Link href={policyHref} target=\"_blank\" rel=\"noopener noreferrer\">{policyText}</Link>;\n\n return (\n <NoticeRoot\n role=\"region\"\n aria-label={t('legal.cookies.region')}\n elevation={8}\n ownSx={{\n position: 'fixed', zIndex: NOTICE_Z_INDEX, left: {xs: 16, sm: 24}, right: {xs: 16, sm: 'auto'}, maxWidth: {sm: NOTICE_MAX_WIDTH},\n bottom: `calc(16px + env(safe-area-inset-bottom, 0px) + var(${FLOATING_BOTTOM_SPACE_CSS_VARIABLE}, 0px))`,\n p: 2.5, borderRadius: 3,\n }}\n sx={sx}\n >\n <NoticeText variant=\"body2\" ownSx={{mb: 2}}>\n {text ?? t('legal.cookies.text')} {policyLink}\n </NoticeText>\n <NoticeActions direction=\"row\" ownSx={{gap: 1, justifyContent: 'flex-end', flexWrap: 'wrap'}}>\n {actions}\n <Button variant=\"contained\" onClick={() => {\n accept();\n onAccept?.({version, acceptedAt: new Date().toISOString()});\n }}>\n {acceptLabel ?? t('legal.cookies.accept')}\n </Button>\n </NoticeActions>\n </NoticeRoot>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,aAAa,UAAU,YAAY,QAAQ,KAAK;AACtD,MAAM,aAAa,UAAU,YAAY,QAAQ,UAAU;AAC3D,MAAM,gBAAgB,UAAU,YAAY,WAAW,KAAK;AAG5D,MAAM,mBAAmB;AAEzB,MAAM,iBAAiB;AAgBvB,SAAwB,aAAa,OAAO;AACxC,QAAM;AAAA,IACF,UAAU;AAAA,IAAK,aAAa;AAAA,IAA4B;AAAA,IAAM;AAAA,IAAY;AAAA,IAAe;AAAA,IAAa;AAAA,IAAa;AAAA,IAAU;AAAA,IAAS;AAAA,IACtI;AAAA,IACA,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,aAAa,OAAA,IAAU,iBAAiB,EAAC,SAAS,YAAW;AAGpE,MAAI,aAAa;AACb,WAAO;AAAA,EACX;AAEA,QAAM,aAAa,eAAe,EAAE,sBAAsB;AAC1D,QAAM,aAAa,gBACb,oBAAC,MAAA,EAAK,WAAU,UAAS,MAAK,UAAS,SAAS,eAAe,IAAI,EAAC,eAAe,cAAc,UAAA,WAAA,CAAW,IAC5G,cAAc,oBAAC,MAAA,EAAK,MAAM,YAAY,QAAO,UAAS,KAAI,uBAAuB,UAAA,WAAA,CAAW;AAElG,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,MAAK;AAAA,MACL,cAAY,EAAE,sBAAsB;AAAA,MACpC,WAAW;AAAA,MACX,OAAO;AAAA,QACH,UAAU;AAAA,QAAS,QAAQ;AAAA,QAAgB,MAAM,EAAC,IAAI,IAAI,IAAI,GAAA;AAAA,QAAK,OAAO,EAAC,IAAI,IAAI,IAAI,OAAA;AAAA,QAAS,UAAU,EAAC,IAAI,iBAAA;AAAA,QAC/G,QAAQ,sDAAsD,kCAAkC;AAAA,QAChG,GAAG;AAAA,QAAK,cAAc;AAAA,MAAA;AAAA,MAE1B;AAAA,MAEA,UAAA;AAAA,QAAA,qBAAC,cAAW,SAAQ,SAAQ,OAAO,EAAC,IAAI,KACnC,UAAA;AAAA,UAAA,QAAQ,EAAE,oBAAoB;AAAA,UAAE;AAAA,UAAE;AAAA,QAAA,GACvC;AAAA,QACA,qBAAC,eAAA,EAAc,WAAU,OAAM,OAAO,EAAC,KAAK,GAAG,gBAAgB,YAAY,UAAU,OAAA,GAChF,UAAA;AAAA,UAAA;AAAA,UACD,oBAAC,QAAA,EAAO,SAAQ,aAAY,SAAS,MAAM;AACvC,mBAAA;AACA,uBAAW,EAAC,SAAS,aAAY,oBAAI,QAAO,YAAA,GAAc;AAAA,UAC9D,GACK,UAAA,eAAe,EAAE,sBAAsB,EAAA,CAC5C;AAAA,QAAA,EAAA,CACJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGZ;"}
|