@stackcraftmx/calcita 0.47.0 → 0.51.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 +173 -12
- package/cli/calcita.js +15 -0
- package/cli/checkTexts.js +209 -0
- package/cli/packageVersions.js +6 -1
- package/cli/projectFiles.js +4 -2
- package/cli/templates/project/README.md.tpl +21 -1
- package/cli/templates/project/eslint.config.js.tpl +27 -0
- package/cli/templates/project/src/app/appTexts.js.tpl +2 -1
- package/cli/templates/project/src/core/publicPaths.js.tpl +10 -2
- package/cli/templates/project/vite.config.js.tpl +8 -1
- package/dist/DataTable/AuditTrailDialog.js +3 -2
- package/dist/DataTable/AuditTrailDialog.js.map +1 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -2
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -2
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +3 -2
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
- package/dist/DataTable/EmptyState.js +3 -2
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +3 -2
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +3 -2
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +95 -28
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/ServerTableFilterDrawer.js +8 -3
- package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -1
- package/dist/DataTable/StackedCell.js +60 -0
- package/dist/DataTable/StackedCell.js.map +1 -0
- package/dist/DataTable/TableCardsView.js +88 -0
- package/dist/DataTable/TableCardsView.js.map +1 -0
- package/dist/DataTable/columnWidths.js +30 -0
- package/dist/DataTable/columnWidths.js.map +1 -0
- package/dist/DataTable/columns.js +47 -1
- package/dist/DataTable/columns.js.map +1 -1
- package/dist/DataTable/fixedColumns.js +24 -0
- package/dist/DataTable/fixedColumns.js.map +1 -0
- package/dist/DataTable/useTableView.js +29 -0
- package/dist/DataTable/useTableView.js.map +1 -0
- package/dist/access/PermissionMatrix.js +3 -2
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/account/AccountProvider.js +1 -1
- package/dist/account/AccountSecuritySection.js +3 -2
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/DemoTotpCodeButton.js +3 -2
- package/dist/account/DemoTotpCodeButton.js.map +1 -1
- package/dist/account/ProfileScreen.js +3 -2
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +3 -2
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/appShell/AccountMenu.js +3 -2
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +3 -2
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +3 -2
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +3 -2
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActionButton.js +11 -4
- package/dist/appShell/BarActionButton.js.map +1 -1
- package/dist/appShell/BarActions.js +3 -2
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarCapsule.js +3 -2
- package/dist/appShell/BarCapsule.js.map +1 -1
- package/dist/appShell/BarSearch.js +3 -2
- package/dist/appShell/BarSearch.js.map +1 -1
- package/dist/appShell/HeaderTools.js +3 -2
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MobileTabBar.js +3 -2
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +3 -2
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +3 -2
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +3 -2
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +3 -2
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TabBarEditor.js +3 -2
- package/dist/appShell/TabBarEditor.js.map +1 -1
- package/dist/appShell/TopMenu.js +3 -2
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +3 -2
- package/dist/appShell/Wordmark.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +3 -2
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +3 -2
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +3 -2
- package/dist/calendar/EventDetails.js.map +1 -1
- package/dist/datePickers/DateInput.js +28 -22
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +33 -27
- package/dist/datePickers/DateTimeInput.js.map +1 -1
- package/dist/datePickers/MonthInput.js +27 -21
- package/dist/datePickers/MonthInput.js.map +1 -1
- package/dist/datePickers/TimeInput.js +26 -20
- package/dist/datePickers/TimeInput.js.map +1 -1
- package/dist/datePickers/usePickerField.js +5 -4
- package/dist/datePickers/usePickerField.js.map +1 -1
- package/dist/downloads/DownloadManager.js +3 -2
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/feedback/ConfirmDialog.js +3 -2
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +3 -2
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +3 -2
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +3 -2
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/forms/FormProblemAlert.js +3 -2
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/SubmitButton.js +7 -2
- package/dist/forms/SubmitButton.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +3 -2
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/fieldRules.js +30 -0
- package/dist/forms/fieldRules.js.map +1 -0
- package/dist/forms/security/SecondStepForm.js +3 -2
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +3 -2
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/forms/useValidationText.js +18 -0
- package/dist/forms/useValidationText.js.map +1 -0
- package/dist/forms/validation.js +35 -31
- package/dist/forms/validation.js.map +1 -1
- package/dist/forms/validationMessages.js +64 -7
- package/dist/forms/validationMessages.js.map +1 -1
- package/dist/forms.js +3 -0
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormProblemAlert.js +3 -2
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/hookForm/SubmitButton.js +7 -2
- package/dist/hookForm/SubmitButton.js.map +1 -1
- package/dist/inbox/InboxLayout.js +3 -2
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +3 -2
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +3 -2
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/index.js +186 -163
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +32 -25
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +8 -5
- package/dist/inputs/CheckboxGroupInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +7 -4
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FieldFrame.js +10 -4
- package/dist/inputs/FieldFrame.js.map +1 -1
- package/dist/inputs/FormSection.js +1 -1
- package/dist/inputs/MaskedInput.js +1 -0
- package/dist/inputs/MaskedInput.js.map +1 -1
- package/dist/inputs/NumberInput.js +29 -24
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/RepeatingFields.js +8 -5
- package/dist/inputs/RepeatingFields.js.map +1 -1
- package/dist/inputs/SelectInput.js +8 -5
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +5 -3
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/TransferListInput.js +8 -5
- package/dist/inputs/TransferListInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +3 -2
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/kanban/KanbanBoard.js +3 -2
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/legal/CookieNotice.js +3 -2
- package/dist/legal/CookieNotice.js.map +1 -1
- package/dist/legal/TermsAcceptanceDialog.js +3 -2
- package/dist/legal/TermsAcceptanceDialog.js.map +1 -1
- package/dist/locales/en.js +24 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +24 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/maps/MapView.js +3 -2
- package/dist/maps/MapView.js.map +1 -1
- package/dist/mexico/CfdiSummary.js +3 -2
- package/dist/mexico/CfdiSummary.js.map +1 -1
- package/dist/mexico/EfirmaFields.js +3 -2
- package/dist/mexico/EfirmaFields.js.map +1 -1
- package/dist/mexico/mexicoIdentifiers.js +5 -0
- package/dist/mexico/mexicoIdentifiers.js.map +1 -1
- package/dist/mexico/mexicoRules.js +9 -4
- package/dist/mexico/mexicoRules.js.map +1 -1
- package/dist/mexico.js +4 -2
- package/dist/pwa/InstallAppSetting.js +3 -2
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +3 -2
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +3 -2
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/react-hook-form.js +3 -0
- package/dist/react-hook-form.js.map +1 -1
- package/dist/richText/RichTextEditor.js +7 -4
- package/dist/richText/RichTextEditor.js.map +1 -1
- package/dist/richText/RichTextView.js +3 -2
- package/dist/richText/RichTextView.js.map +1 -1
- package/dist/security/ActivityLog.js +3 -2
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +3 -2
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +3 -2
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/table.js +21 -2
- package/dist/table.js.map +1 -1
- package/dist/theme/themePieces.js +70 -4
- package/dist/theme/themePieces.js.map +1 -1
- package/dist/toolkit/ToolkitProvider.js +8 -1
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/browserNavigation.js +19 -4
- package/dist/toolkit/browserNavigation.js.map +1 -1
- package/dist/toolkit/combineTexts.js +32 -0
- package/dist/toolkit/combineTexts.js.map +1 -0
- package/dist/toolkit/hashNavigation.js +17 -3
- package/dist/toolkit/hashNavigation.js.map +1 -1
- package/dist/toolkit/navigationPaths.js +15 -0
- package/dist/toolkit/navigationPaths.js.map +1 -1
- package/dist/toolkit/pathIndex.js +66 -0
- package/dist/toolkit/pathIndex.js.map +1 -0
- package/dist/toolkit/toolkitSettings.js +19 -1
- package/dist/toolkit/toolkitSettings.js.map +1 -1
- package/dist/types/DataTable/AuditTrailDialog.d.ts +6 -17
- package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +6 -5
- package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +6 -9
- package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +6 -14
- package/dist/types/DataTable/EmptyState.d.ts +6 -9
- package/dist/types/DataTable/FilterChips.d.ts +6 -11
- package/dist/types/DataTable/GoToPage.d.ts +6 -9
- package/dist/types/DataTable/ServerTable.d.ts +16 -80
- package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +6 -13
- package/dist/types/DataTable/StackedCell.d.ts +12 -0
- package/dist/types/DataTable/TableCardsView.d.ts +27 -0
- package/dist/types/DataTable/columnWidths.d.ts +21 -0
- package/dist/types/DataTable/columns.d.ts +111 -0
- package/dist/types/DataTable/fixedColumns.d.ts +29 -0
- package/dist/types/DataTable/index.d.ts +6 -1
- package/dist/types/DataTable/useTableView.d.ts +22 -0
- package/dist/types/access/PermissionMatrix.d.ts +6 -10
- package/dist/types/account/AccountSecuritySection.d.ts +6 -7
- package/dist/types/account/DemoTotpCodeButton.d.ts +6 -5
- package/dist/types/account/ProfileScreen.d.ts +6 -18
- package/dist/types/account/SignInScreen.d.ts +6 -13
- package/dist/types/appShell/AccountMenu.d.ts +6 -11
- package/dist/types/appShell/AppLayout.d.ts +3 -40
- package/dist/types/appShell/AppearanceSettings.d.ts +6 -12
- package/dist/types/appShell/AuthLayout.d.ts +3 -11
- package/dist/types/appShell/BarActionButton.d.ts +6 -10
- package/dist/types/appShell/BarActions.d.ts +6 -15
- package/dist/types/appShell/BarCapsule.d.ts +6 -9
- package/dist/types/appShell/BarSearch.d.ts +6 -15
- package/dist/types/appShell/HeaderTools.d.ts +6 -16
- package/dist/types/appShell/MobileTabBar.d.ts +6 -13
- package/dist/types/appShell/SettingsMenu.d.ts +6 -10
- package/dist/types/appShell/SideMenu.d.ts +6 -20
- package/dist/types/appShell/SideMenuEntry.d.ts +6 -11
- package/dist/types/appShell/SubmenuActionsMenu.d.ts +6 -12
- package/dist/types/appShell/TabBarEditor.d.ts +6 -9
- package/dist/types/appShell/TopMenu.d.ts +6 -5
- package/dist/types/appShell/Wordmark.d.ts +6 -7
- package/dist/types/auth/PasswordChecklist.d.ts +6 -9
- package/dist/types/calendar/EventCalendar.d.ts +6 -23
- package/dist/types/calendar/EventDetails.d.ts +6 -10
- package/dist/types/datePickers/DateInput.d.ts +7 -22
- package/dist/types/datePickers/DateTimeInput.d.ts +7 -27
- package/dist/types/datePickers/MonthInput.d.ts +7 -21
- package/dist/types/datePickers/TimeInput.d.ts +7 -21
- package/dist/types/datePickers/usePickerField.d.ts +5 -1
- package/dist/types/downloads/DownloadManager.d.ts +6 -6
- package/dist/types/feedback/ConfirmDialog.d.ts +6 -18
- package/dist/types/feedback/ModalWaiting.d.ts +6 -9
- package/dist/types/feedback/NewBuildNotice.d.ts +6 -10
- package/dist/types/filepond/FileDropzone.d.ts +6 -29
- package/dist/types/forms/FormProblemAlert.d.ts +6 -5
- package/dist/types/forms/SubmitButton.d.ts +6 -6
- package/dist/types/forms/auth/SignInForm.d.ts +6 -11
- package/dist/types/forms/fieldRules.d.ts +44 -0
- package/dist/types/forms/index.d.ts +1 -0
- package/dist/types/forms/security/SecondStepForm.d.ts +6 -14
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +6 -21
- package/dist/types/forms/useValidationText.d.ts +12 -0
- package/dist/types/forms/validation.d.ts +117 -174
- package/dist/types/forms/validationMessages.d.ts +54 -6
- package/dist/types/forms.d.ts +1 -1
- package/dist/types/hookForm/FormProblemAlert.d.ts +6 -5
- package/dist/types/hookForm/SubmitButton.d.ts +6 -13
- package/dist/types/inbox/InboxLayout.d.ts +6 -17
- package/dist/types/inbox/MessageList.d.ts +6 -18
- package/dist/types/inbox/MessageView.d.ts +6 -14
- package/dist/types/index.d.ts +8 -1
- package/dist/types/inputs/CatalogInput.d.ts +7 -24
- package/dist/types/inputs/CheckboxGroupInput.d.ts +7 -18
- package/dist/types/inputs/ColorInput.d.ts +7 -16
- package/dist/types/inputs/FieldFrame.d.ts +6 -10
- package/dist/types/inputs/MaskedInput.d.ts +3 -0
- package/dist/types/inputs/NumberInput.d.ts +7 -20
- package/dist/types/inputs/RepeatingFields.d.ts +7 -28
- package/dist/types/inputs/SelectInput.d.ts +7 -20
- package/dist/types/inputs/TextInput.d.ts +3 -0
- package/dist/types/inputs/TransferListInput.d.ts +7 -20
- package/dist/types/inputs/WizardLayout.d.ts +6 -19
- package/dist/types/kanban/KanbanBoard.d.ts +6 -25
- package/dist/types/legal/CookieNotice.d.ts +6 -16
- package/dist/types/legal/TermsAcceptanceDialog.d.ts +6 -18
- package/dist/types/locales/en.d.ts +24 -0
- package/dist/types/locales/es.d.ts +24 -0
- package/dist/types/maps/MapView.d.ts +6 -17
- package/dist/types/mexico/CfdiSummary.d.ts +6 -11
- package/dist/types/mexico/EfirmaFields.d.ts +6 -16
- package/dist/types/mexico/mexicoIdentifiers.d.ts +7 -0
- package/dist/types/mexico/mexicoRules.d.ts +19 -3
- package/dist/types/mexico.d.ts +2 -2
- package/dist/types/pwa/InstallAppSetting.d.ts +6 -6
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +6 -9
- package/dist/types/pwa/InstallPromptBanner.d.ts +6 -11
- package/dist/types/react-hook-form.d.ts +1 -0
- package/dist/types/richText/RichTextEditor.d.ts +7 -22
- package/dist/types/richText/RichTextView.d.ts +6 -9
- package/dist/types/security/ActivityLog.d.ts +6 -9
- package/dist/types/security/RecoveryCodesDialog.d.ts +6 -11
- package/dist/types/security/SessionsCard.d.ts +6 -11
- package/dist/types/table.d.ts +3 -1
- package/dist/types/theme/themePieces.d.ts +55 -3
- package/dist/types/toolkit/ToolkitProvider.d.ts +13 -1
- package/dist/types/toolkit/browserNavigation.d.ts +7 -3
- package/dist/types/toolkit/combineTexts.d.ts +6 -0
- package/dist/types/toolkit/hashNavigation.d.ts +4 -2
- package/dist/types/toolkit/navigationPaths.d.ts +35 -0
- package/dist/types/toolkit/pathIndex.d.ts +27 -0
- package/dist/types/toolkit/toolkitSettings.d.ts +7 -4
- package/dist/types/ui/actions/ActionsMenu.d.ts +9 -11
- package/dist/types/ui/actions/CopyButton.d.ts +6 -12
- package/dist/types/ui/actions/DownloadMenu.d.ts +6 -29
- package/dist/types/ui/actions/SecretField.d.ts +6 -11
- package/dist/types/ui/actions/SelectionActions.d.ts +6 -13
- package/dist/types/ui/cards/RecordCard.d.ts +6 -31
- package/dist/types/ui/cards/RecordCardSkeleton.d.ts +6 -11
- package/dist/types/ui/countdown/Countdown.d.ts +6 -15
- package/dist/types/ui/documents/PdfViewerDialog.d.ts +6 -13
- package/dist/types/ui/filters/DateRangeFilter.d.ts +6 -10
- package/dist/types/ui/filters/FilterDrawer.d.ts +6 -17
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +6 -13
- package/dist/types/ui/filters/QuickFilters.d.ts +6 -12
- package/dist/types/ui/help/HelpButton.d.ts +6 -10
- package/dist/types/ui/help/HelpCarouselDialog.d.ts +6 -14
- package/dist/types/ui/labels/MarkedText.d.ts +10 -0
- package/dist/types/ui/labels/Tag.d.ts +6 -12
- package/dist/types/ui/labels/markedTextPieces.d.ts +22 -0
- package/dist/types/ui/layout/CollapsibleSection.d.ts +6 -12
- package/dist/types/ui/layout/ContentDialog.d.ts +6 -10
- package/dist/types/ui/layout/DefinitionList.d.ts +6 -9
- package/dist/types/ui/layout/DetailHeader.d.ts +6 -10
- package/dist/types/ui/layout/ErrorPage.d.ts +6 -17
- package/dist/types/ui/layout/FullPageLoader.d.ts +6 -6
- package/dist/types/ui/layout/GlassPanel.d.ts +6 -8
- package/dist/types/ui/layout/PageHeader.d.ts +6 -10
- package/dist/types/ui/layout/PageHeading.d.ts +6 -11
- package/dist/types/ui/layout/ProfileHeader.d.ts +6 -11
- package/dist/types/ui/layout/ScrollableRow.d.ts +6 -13
- package/dist/types/ui/layout/SectionCard.d.ts +6 -8
- package/dist/types/ui/metrics/BarList.d.ts +6 -12
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +6 -19
- package/dist/types/ui/metrics/ChartTooltip.d.ts +6 -6
- package/dist/types/ui/metrics/ColumnChart.d.ts +6 -25
- package/dist/types/ui/metrics/LineChart.d.ts +6 -20
- package/dist/types/ui/metrics/MetricCard.d.ts +6 -17
- package/dist/types/ui/metrics/TableToggle.d.ts +6 -6
- package/dist/types/ui/metrics/chartParts.d.ts +15 -30
- package/dist/types/ui/navigation/BackButton.d.ts +6 -9
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +6 -9
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +6 -8
- package/dist/types/ui/navigation/LanguageToggle.d.ts +6 -8
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +6 -7
- package/dist/types/ui/navigation/RouteTabs.d.ts +7 -12
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +6 -23
- package/dist/types/upload/AttachmentsInput.d.ts +11 -1
- package/dist/types/upload/FileDropzone.d.ts +6 -21
- package/dist/types/upload/FileInput.d.ts +7 -24
- package/dist/types/upload/FileUploadInput.d.ts +7 -31
- package/dist/types/upload/ImageInput.d.ts +7 -25
- package/dist/types/upload/TemplateDownloadButton.d.ts +6 -18
- package/dist/ui/actions/ActionsMenu.js +14 -5
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/CopyButton.js +8 -3
- package/dist/ui/actions/CopyButton.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +3 -2
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SecretField.js +8 -3
- package/dist/ui/actions/SecretField.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +3 -2
- package/dist/ui/actions/SelectionActions.js.map +1 -1
- package/dist/ui/cards/RecordCard.js +4 -3
- package/dist/ui/cards/RecordCard.js.map +1 -1
- package/dist/ui/cards/RecordCardSkeleton.js +8 -3
- package/dist/ui/cards/RecordCardSkeleton.js.map +1 -1
- package/dist/ui/countdown/Countdown.js +3 -2
- package/dist/ui/countdown/Countdown.js.map +1 -1
- package/dist/ui/documents/PdfViewerDialog.js +3 -2
- package/dist/ui/documents/PdfViewerDialog.js.map +1 -1
- package/dist/ui/filters/DateRangeFilter.js +3 -2
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/FilterDrawer.js +3 -2
- package/dist/ui/filters/FilterDrawer.js.map +1 -1
- package/dist/ui/filters/PeriodRangeSelector.js +3 -2
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +3 -2
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/help/HelpButton.js +8 -3
- package/dist/ui/help/HelpButton.js.map +1 -1
- package/dist/ui/help/HelpCarouselDialog.js +3 -2
- package/dist/ui/help/HelpCarouselDialog.js.map +1 -1
- package/dist/ui/labels/MarkedText.js +31 -0
- package/dist/ui/labels/MarkedText.js.map +1 -0
- package/dist/ui/labels/Tag.js +3 -2
- package/dist/ui/labels/Tag.js.map +1 -1
- package/dist/ui/labels/markedTextPieces.js +64 -0
- package/dist/ui/labels/markedTextPieces.js.map +1 -0
- package/dist/ui/layout/CollapsibleSection.js +3 -2
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +3 -2
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/DefinitionList.js +3 -2
- package/dist/ui/layout/DefinitionList.js.map +1 -1
- package/dist/ui/layout/DetailHeader.js +3 -2
- package/dist/ui/layout/DetailHeader.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +3 -2
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +3 -2
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/GlassPanel.js +3 -2
- package/dist/ui/layout/GlassPanel.js.map +1 -1
- package/dist/ui/layout/PageHeader.js +3 -2
- package/dist/ui/layout/PageHeader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +3 -2
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/layout/ProfileHeader.js +3 -2
- package/dist/ui/layout/ProfileHeader.js.map +1 -1
- package/dist/ui/layout/ScrollableRow.js +3 -2
- package/dist/ui/layout/ScrollableRow.js.map +1 -1
- package/dist/ui/layout/SectionCard.js +3 -2
- package/dist/ui/layout/SectionCard.js.map +1 -1
- package/dist/ui/metrics/BarList.js +3 -2
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +3 -2
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js +3 -2
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +3 -2
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +3 -2
- package/dist/ui/metrics/LineChart.js.map +1 -1
- package/dist/ui/metrics/MetricCard.js +3 -2
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +3 -2
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +7 -4
- package/dist/ui/metrics/chartParts.js.map +1 -1
- package/dist/ui/navigation/BackButton.js +7 -2
- package/dist/ui/navigation/BackButton.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +3 -2
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
- package/dist/ui/navigation/FontSizeToggle.js +3 -2
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
- package/dist/ui/navigation/LanguageToggle.js +3 -2
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +3 -2
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/navigation/RouteTabs.js +8 -4
- package/dist/ui/navigation/RouteTabs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +3 -2
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/AttachmentsInput.js +9 -6
- package/dist/upload/AttachmentsInput.js.map +1 -1
- package/dist/upload/FileChip.js +2 -1
- package/dist/upload/FileChip.js.map +1 -1
- package/dist/upload/FileDropzone.js +5 -4
- package/dist/upload/FileDropzone.js.map +1 -1
- package/dist/upload/FileInput.js +9 -6
- package/dist/upload/FileInput.js.map +1 -1
- package/dist/upload/FileUploadInput.js +9 -6
- package/dist/upload/FileUploadInput.js.map +1 -1
- package/dist/upload/ImageInput.js +8 -5
- package/dist/upload/ImageInput.js.map +1 -1
- package/dist/upload/TemplateDownloadButton.js +3 -2
- package/dist/upload/TemplateDownloadButton.js.map +1 -1
- package/package.json +1 -1
- package/cli/templates/project/src/app/combineTexts.js.tpl +0 -31
|
@@ -10,7 +10,7 @@ import Typography from "@mui/material/Typography";
|
|
|
10
10
|
import TextInput from "../inputs/TextInput.js";
|
|
11
11
|
import { CERTIFICATE_STATUS, certificateStatusOf, readCertificate } from "./certificateReader.js";
|
|
12
12
|
import { verifyEfirmaKey, EfirmaKeyError } from "./efirmaKey.js";
|
|
13
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
13
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
14
14
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
15
15
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
16
16
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
@@ -27,7 +27,7 @@ const STATUS_COLORS = Object.freeze({
|
|
|
27
27
|
});
|
|
28
28
|
const VERIFY_STATE = Object.freeze({ idle: "idle", verifying: "verifying", verified: "verified", failed: "failed" });
|
|
29
29
|
const EMPTY_VALUE = Object.freeze({ certificateFile: null, keyFile: null, password: "" });
|
|
30
|
-
function
|
|
30
|
+
function EfirmaFieldsContent(props) {
|
|
31
31
|
const {
|
|
32
32
|
value = EMPTY_VALUE,
|
|
33
33
|
onChange,
|
|
@@ -161,6 +161,7 @@ function EfirmaFields(props) {
|
|
|
161
161
|
] })
|
|
162
162
|
] });
|
|
163
163
|
}
|
|
164
|
+
const EfirmaFields = withPieceSlots(EfirmaFieldsContent, THEME_NAME);
|
|
164
165
|
export {
|
|
165
166
|
EfirmaFields as default
|
|
166
167
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EfirmaFields.js","sources":["../../src/mexico/EfirmaFields.jsx"],"sourcesContent":["// La e.firma (o el sello digital) de una persona o una empresa: el certificado (.cer), la llave privada (.key) y su\n// contraseña, con lo que dice el certificado a la vista (RFC, nombre, número de certificado, vigencia), leído en el navegador\n// sin subirlo.\n//\n// Avisa sin esperar al servidor: un .cer que no es certificado, uno vencido o que todavía no vale, y un RFC distinto al que se\n// espera (expectedRfc: el de la empresa).\n//\n// «Revisar la e.firma» abre la llave con su contraseña y comprueba que es la de ese certificado, en el navegador\n// (verifyEfirmaKey, con jsrsasign: la llave del SAT viene cifrada con 3DES, que el navegador no trae), y dice el motivo de un\n// «no»: la contraseña, otra llave o un archivo que no es llave. Con onVerifyKey la revisión es del proyecto (su API con\n// OpenSSL, por ejemplo). Calcita no manda la llave ni la contraseña a ningún lado por su cuenta.\n//\n// Controlado: value {certificateFile, keyFile, password} y onChange con el valor completo.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaEfirmaFields con defaultProps y\n// styleOverrides de sus partes: root, certificate, verify.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {CERTIFICATE_STATUS, certificateStatusOf, readCertificate} from '@/mexico/certificateReader.js';\nimport {EfirmaKeyError, verifyEfirmaKey} from '@/mexico/efirmaKey.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport DefinitionList from '@/ui/layout/DefinitionList.jsx';\nimport FileInput from '@/upload/FileInput.jsx';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaEfirmaFields)\nconst THEME_NAME = 'CalcitaEfirmaFields';\nconst EfirmaRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst EfirmaCertificate = piecePart(THEME_NAME, 'Certificate', Paper);\nconst EfirmaVerify = piecePart(THEME_NAME, 'Verify', Stack);\n\n// el color del chip de la vigencia\nconst STATUS_COLORS = Object.freeze({\n [CERTIFICATE_STATUS.valid]: 'success', [CERTIFICATE_STATUS.expired]: 'error', [CERTIFICATE_STATUS.notYetValid]: 'warning',\n});\n// en qué va la revisión de la llave\nconst VERIFY_STATE = Object.freeze({idle: 'idle', verifying: 'verifying', verified: 'verified', failed: 'failed'});\nconst EMPTY_VALUE = Object.freeze({certificateFile: null, keyFile: null, password: ''});\n\n/**\n * @param {object} props\n * @param {{certificateFile: File|null, keyFile: File|null, password: string}} [props.value]\n * @param {(value: {certificateFile: File|null, keyFile: File|null, password: string}) => void} props.onChange el valor completo\n * @param {string} [props.expectedRfc] el RFC que debe traer el certificado (el de la empresa); si es otro, se avisa\n * @param {(certificate: ReturnType<typeof import('@/mexico/certificateReader.js').readCertificate>|null) => void} [props.onCertificateRead] lo que dice el certificado al elegirlo (null al quitarlo\n * o si no se pudo leer)\n * @param {(input: {certificateFile: File, keyFile: File, password: string, certificate: ReturnType<typeof import('@/mexico/certificateReader.js').readCertificate>}) => Promise<void>} [props.onVerifyKey]\n * la revisión del proyecto (su API); si truena, su mensaje se enseña. Sin él, se abre en el navegador con jsrsasign\n * @param {string} [props.verifyLabel] ya traducido; por omisión «Revisar la e.firma»\n * @param {boolean} [props.disabled]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `mexico` en los textos del paquete\n */\nexport default function EfirmaFields(props) {\n const {\n value = EMPTY_VALUE, onChange, expectedRfc, onCertificateRead, onVerifyKey, verifyLabel, disabled = false, sx, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('mexico', texts);\n const {formatDate} = useFormatters();\n const [certificate, setCertificate] = useState(null);\n const [readError, setReadError] = useState(null);\n const [verify, setVerify] = useState({state: VERIFY_STATE.idle, message: null});\n\n // cualquier cambio deja la revisión de la llave sin hacer: ya no es la misma combinación\n const change = (changes) => {\n setVerify({state: VERIFY_STATE.idle, message: null});\n onChange({...value, ...changes});\n };\n\n const chooseCertificate = async (certificateFile) => {\n change({certificateFile});\n setReadError(null);\n if (!certificateFile) {\n setCertificate(null);\n onCertificateRead?.(null);\n return;\n }\n try {\n const read = readCertificate(new Uint8Array(await certificateFile.arrayBuffer()));\n setCertificate(read);\n onCertificateRead?.(read);\n } catch {\n setCertificate(null);\n setReadError(t('efirma.notCertificate'));\n onCertificateRead?.(null);\n }\n };\n\n // sin revisión del proyecto, la del navegador, con el motivo de un «no» en palabras de la persona\n const verifyInBrowser = async ({certificateFile, keyFile, password}) => {\n try {\n await verifyEfirmaKey({certificateBytes: await certificateFile.arrayBuffer(), keyBytes: await keyFile.arrayBuffer(), password});\n } catch (error) {\n throw new Error(error instanceof EfirmaKeyError ? t(`efirma.problems.${error.reason}`) : t('efirma.verifyFailed'));\n }\n };\n\n const verifyKey = async () => {\n setVerify({state: VERIFY_STATE.verifying, message: null});\n try {\n await (onVerifyKey ?? verifyInBrowser)({\n certificateFile: value.certificateFile, keyFile: value.keyFile, password: value.password, certificate,\n });\n setVerify({state: VERIFY_STATE.verified, message: null});\n } catch (error) {\n setVerify({state: VERIFY_STATE.failed, message: error?.message ?? t('efirma.verifyFailed')});\n }\n };\n\n const status = certificate ? certificateStatusOf(certificate) : null;\n const isOtherRfc = Boolean(certificate && expectedRfc) && certificate.rfc?.toUpperCase() !== expectedRfc.toUpperCase();\n const canVerify = Boolean(certificate && value.keyFile && value.password) && status === CERTIFICATE_STATUS.valid && !disabled;\n\n return (\n <EfirmaRoot ownSx={{gap: 1}} sx={sx}>\n <FileInput label={t('efirma.certificate')} value={value.certificateFile} onChange={chooseCertificate} accept=\".cer\" disabled={disabled}\n error={readError ?? undefined} helperText={t('efirma.certificateHelp')} texts={texts} />\n <FileInput label={t('efirma.privateKey')} value={value.keyFile} onChange={(keyFile) => change({keyFile})} accept=\".key\" disabled={disabled}\n helperText={t('efirma.privateKeyHelp')} texts={texts} />\n <TextInput label={t('efirma.password')} type=\"password\" value={value.password} onChange={(password) => change({password})}\n autoComplete=\"off\" disabled={disabled} helperText={t('efirma.passwordHelp')} />\n\n {certificate && (\n <EfirmaCertificate variant=\"outlined\" ownSx={{p: 2, borderRadius: 2}}>\n <Stack direction=\"row\" sx={{alignItems: 'center', gap: 1, mb: 1.5, flexWrap: 'wrap'}}>\n <Typography variant=\"subtitle1\" component=\"p\" sx={{mr: 'auto'}}>{certificate.name ?? certificate.organization ?? '—'}</Typography>\n <Chip size=\"small\" color={STATUS_COLORS[status]} label={t(`efirma.status.${status}`)} />\n </Stack>\n {isOtherRfc && <Alert severity=\"warning\" sx={{mb: 1.5}}>{t('efirma.otherRfc', {rfc: certificate.rfc, expected: expectedRfc})}</Alert>}\n <DefinitionList entries={[\n {label: t('efirma.rfc'), value: certificate.rfc ?? '—'},\n ...(certificate.representativeRfc ? [{label: t('efirma.representative'), value: [certificate.representativeRfc, certificate.representativeCurp].filter(Boolean).join(' · ')}] : []),\n ...(certificate.curp ? [{label: t('efirma.curp'), value: certificate.curp}] : []),\n {label: t('efirma.certificateNumber'), value: certificate.certificateNumber},\n {label: t('efirma.validity'), value: t('efirma.validityRange', {from: formatDate(certificate.validFrom), to: formatDate(certificate.validTo)})},\n ...(certificate.branch ? [{label: t('efirma.branch'), value: certificate.branch}] : []),\n ]} />\n </EfirmaCertificate>\n )}\n\n <EfirmaVerify direction=\"row\" ownSx={{gap: 1.5, alignItems: 'center', flexWrap: 'wrap', mt: 1}}>\n <Button variant=\"outlined\" onClick={verifyKey} disabled={!canVerify || verify.state === VERIFY_STATE.verifying}\n loading={verify.state === VERIFY_STATE.verifying}>\n {verifyLabel ?? t('efirma.verify')}\n </Button>\n {verify.state === VERIFY_STATE.verified && <Alert severity=\"success\" sx={{py: 0}}>{t('efirma.verified')}</Alert>}\n {verify.state === VERIFY_STATE.failed && <Alert severity=\"error\" role=\"alert\" sx={{py: 0}}>{verify.message}</Alert>}\n </EfirmaVerify>\n </EfirmaRoot>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EfirmaFields.js","sources":["../../src/mexico/EfirmaFields.jsx"],"sourcesContent":["// La e.firma (o el sello digital) de una persona o una empresa: el certificado (.cer), la llave privada (.key) y su\n// contraseña, con lo que dice el certificado a la vista (RFC, nombre, número de certificado, vigencia), leído en el navegador\n// sin subirlo.\n//\n// Avisa sin esperar al servidor: un .cer que no es certificado, uno vencido o que todavía no vale, y un RFC distinto al que se\n// espera (expectedRfc: el de la empresa).\n//\n// «Revisar la e.firma» abre la llave con su contraseña y comprueba que es la de ese certificado, en el navegador\n// (verifyEfirmaKey, con jsrsasign: la llave del SAT viene cifrada con 3DES, que el navegador no trae), y dice el motivo de un\n// «no»: la contraseña, otra llave o un archivo que no es llave. Con onVerifyKey la revisión es del proyecto (su API con\n// OpenSSL, por ejemplo). Calcita no manda la llave ni la contraseña a ningún lado por su cuenta.\n//\n// Controlado: value {certificateFile, keyFile, password} y onChange con el valor completo.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaEfirmaFields con defaultProps y\n// styleOverrides de sus partes: root, certificate, verify.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {CERTIFICATE_STATUS, certificateStatusOf, readCertificate} from '@/mexico/certificateReader.js';\nimport {EfirmaKeyError, verifyEfirmaKey} from '@/mexico/efirmaKey.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport DefinitionList from '@/ui/layout/DefinitionList.jsx';\nimport FileInput from '@/upload/FileInput.jsx';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaEfirmaFields)\nconst THEME_NAME = 'CalcitaEfirmaFields';\nconst EfirmaRoot = piecePart(THEME_NAME, 'Root', Stack);\nconst EfirmaCertificate = piecePart(THEME_NAME, 'Certificate', Paper);\nconst EfirmaVerify = piecePart(THEME_NAME, 'Verify', Stack);\n\n// el color del chip de la vigencia\nconst STATUS_COLORS = Object.freeze({\n [CERTIFICATE_STATUS.valid]: 'success', [CERTIFICATE_STATUS.expired]: 'error', [CERTIFICATE_STATUS.notYetValid]: 'warning',\n});\n// en qué va la revisión de la llave\nconst VERIFY_STATE = Object.freeze({idle: 'idle', verifying: 'verifying', verified: 'verified', failed: 'failed'});\nconst EMPTY_VALUE = Object.freeze({certificateFile: null, keyFile: null, password: ''});\n\n/**\n * @param {object} props\n * @param {{certificateFile: File|null, keyFile: File|null, password: string}} [props.value]\n * @param {(value: {certificateFile: File|null, keyFile: File|null, password: string}) => void} props.onChange el valor completo\n * @param {string} [props.expectedRfc] el RFC que debe traer el certificado (el de la empresa); si es otro, se avisa\n * @param {(certificate: ReturnType<typeof import('@/mexico/certificateReader.js').readCertificate>|null) => void} [props.onCertificateRead] lo que dice el certificado al elegirlo (null al quitarlo\n * o si no se pudo leer)\n * @param {(input: {certificateFile: File, keyFile: File, password: string, certificate: ReturnType<typeof import('@/mexico/certificateReader.js').readCertificate>}) => Promise<void>} [props.onVerifyKey]\n * la revisión del proyecto (su API); si truena, su mensaje se enseña. Sin él, se abre en el navegador con jsrsasign\n * @param {string} [props.verifyLabel] ya traducido; por omisión «Revisar la e.firma»\n * @param {boolean} [props.disabled]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `mexico` en los textos del paquete\n */\nfunction EfirmaFieldsContent(props) {\n const {\n value = EMPTY_VALUE, onChange, expectedRfc, onCertificateRead, onVerifyKey, verifyLabel, disabled = false, sx, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('mexico', texts);\n const {formatDate} = useFormatters();\n const [certificate, setCertificate] = useState(null);\n const [readError, setReadError] = useState(null);\n const [verify, setVerify] = useState({state: VERIFY_STATE.idle, message: null});\n\n // cualquier cambio deja la revisión de la llave sin hacer: ya no es la misma combinación\n const change = (changes) => {\n setVerify({state: VERIFY_STATE.idle, message: null});\n onChange({...value, ...changes});\n };\n\n const chooseCertificate = async (certificateFile) => {\n change({certificateFile});\n setReadError(null);\n if (!certificateFile) {\n setCertificate(null);\n onCertificateRead?.(null);\n return;\n }\n try {\n const read = readCertificate(new Uint8Array(await certificateFile.arrayBuffer()));\n setCertificate(read);\n onCertificateRead?.(read);\n } catch {\n setCertificate(null);\n setReadError(t('efirma.notCertificate'));\n onCertificateRead?.(null);\n }\n };\n\n // sin revisión del proyecto, la del navegador, con el motivo de un «no» en palabras de la persona\n const verifyInBrowser = async ({certificateFile, keyFile, password}) => {\n try {\n await verifyEfirmaKey({certificateBytes: await certificateFile.arrayBuffer(), keyBytes: await keyFile.arrayBuffer(), password});\n } catch (error) {\n throw new Error(error instanceof EfirmaKeyError ? t(`efirma.problems.${error.reason}`) : t('efirma.verifyFailed'));\n }\n };\n\n const verifyKey = async () => {\n setVerify({state: VERIFY_STATE.verifying, message: null});\n try {\n await (onVerifyKey ?? verifyInBrowser)({\n certificateFile: value.certificateFile, keyFile: value.keyFile, password: value.password, certificate,\n });\n setVerify({state: VERIFY_STATE.verified, message: null});\n } catch (error) {\n setVerify({state: VERIFY_STATE.failed, message: error?.message ?? t('efirma.verifyFailed')});\n }\n };\n\n const status = certificate ? certificateStatusOf(certificate) : null;\n const isOtherRfc = Boolean(certificate && expectedRfc) && certificate.rfc?.toUpperCase() !== expectedRfc.toUpperCase();\n const canVerify = Boolean(certificate && value.keyFile && value.password) && status === CERTIFICATE_STATUS.valid && !disabled;\n\n return (\n <EfirmaRoot ownSx={{gap: 1}} sx={sx}>\n <FileInput label={t('efirma.certificate')} value={value.certificateFile} onChange={chooseCertificate} accept=\".cer\" disabled={disabled}\n error={readError ?? undefined} helperText={t('efirma.certificateHelp')} texts={texts} />\n <FileInput label={t('efirma.privateKey')} value={value.keyFile} onChange={(keyFile) => change({keyFile})} accept=\".key\" disabled={disabled}\n helperText={t('efirma.privateKeyHelp')} texts={texts} />\n <TextInput label={t('efirma.password')} type=\"password\" value={value.password} onChange={(password) => change({password})}\n autoComplete=\"off\" disabled={disabled} helperText={t('efirma.passwordHelp')} />\n\n {certificate && (\n <EfirmaCertificate variant=\"outlined\" ownSx={{p: 2, borderRadius: 2}}>\n <Stack direction=\"row\" sx={{alignItems: 'center', gap: 1, mb: 1.5, flexWrap: 'wrap'}}>\n <Typography variant=\"subtitle1\" component=\"p\" sx={{mr: 'auto'}}>{certificate.name ?? certificate.organization ?? '—'}</Typography>\n <Chip size=\"small\" color={STATUS_COLORS[status]} label={t(`efirma.status.${status}`)} />\n </Stack>\n {isOtherRfc && <Alert severity=\"warning\" sx={{mb: 1.5}}>{t('efirma.otherRfc', {rfc: certificate.rfc, expected: expectedRfc})}</Alert>}\n <DefinitionList entries={[\n {label: t('efirma.rfc'), value: certificate.rfc ?? '—'},\n ...(certificate.representativeRfc ? [{label: t('efirma.representative'), value: [certificate.representativeRfc, certificate.representativeCurp].filter(Boolean).join(' · ')}] : []),\n ...(certificate.curp ? [{label: t('efirma.curp'), value: certificate.curp}] : []),\n {label: t('efirma.certificateNumber'), value: certificate.certificateNumber},\n {label: t('efirma.validity'), value: t('efirma.validityRange', {from: formatDate(certificate.validFrom), to: formatDate(certificate.validTo)})},\n ...(certificate.branch ? [{label: t('efirma.branch'), value: certificate.branch}] : []),\n ]} />\n </EfirmaCertificate>\n )}\n\n <EfirmaVerify direction=\"row\" ownSx={{gap: 1.5, alignItems: 'center', flexWrap: 'wrap', mt: 1}}>\n <Button variant=\"outlined\" onClick={verifyKey} disabled={!canVerify || verify.state === VERIFY_STATE.verifying}\n loading={verify.state === VERIFY_STATE.verifying}>\n {verifyLabel ?? t('efirma.verify')}\n </Button>\n {verify.state === VERIFY_STATE.verified && <Alert severity=\"success\" sx={{py: 0}}>{t('efirma.verified')}</Alert>}\n {verify.state === VERIFY_STATE.failed && <Alert severity=\"error\" role=\"alert\" sx={{py: 0}}>{verify.message}</Alert>}\n </EfirmaVerify>\n </EfirmaRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst EfirmaFields = withPieceSlots(EfirmaFieldsContent, THEME_NAME);\nexport default EfirmaFields;\n"],"names":[],"mappings":"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"}
|
|
@@ -73,12 +73,17 @@ function isValidClabe(clabe) {
|
|
|
73
73
|
const clean = (clabe ?? "").replace(/\s/g, "");
|
|
74
74
|
return CLABE_PATTERN.test(clean) && clabeCheckDigitOf(clean) === clean.at(-1);
|
|
75
75
|
}
|
|
76
|
+
const POSTAL_CODE_PATTERN = /^(?!00)\d{5}$/;
|
|
77
|
+
function isValidPostalCode(postalCode) {
|
|
78
|
+
return POSTAL_CODE_PATTERN.test((postalCode ?? "").replace(/\s/g, ""));
|
|
79
|
+
}
|
|
76
80
|
export {
|
|
77
81
|
RFC_KIND,
|
|
78
82
|
clabeCheckDigitOf,
|
|
79
83
|
curpCheckDigitOf,
|
|
80
84
|
isValidClabe,
|
|
81
85
|
isValidCurp,
|
|
86
|
+
isValidPostalCode,
|
|
82
87
|
isValidRfc,
|
|
83
88
|
rfcCheckDigitOf,
|
|
84
89
|
rfcKindOf
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mexicoIdentifiers.js","sources":["../../src/mexico/mexicoIdentifiers.js"],"sourcesContent":["// Las claves de México que se escriben en casi cualquier sistema: el RFC (SAT), la CURP (RENAPO) y la CLABE (Banxico). Revisan\n// la forma de cada una y su dígito verificador, para avisar ANTES de mandar algo mal escrito. Lo que de verdad vale (que el\n// RFC exista, que la CLABE sea de esa persona) lo dice el SAT o el banco, del lado del servidor.\n//\n// Son estándares nacionales, no reglas de un cliente.\n//\n// Puro (sin React): se prueba con node (mexicoIdentifiers.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n// RFC: 3 letras (persona moral) o 4 (persona física), la fecha AAMMDD y la homoclave de 3 (la última es el dígito)\nconst RFC_PATTERN = /^([A-ZÑ&]{3,4})(\\d{2})(\\d{2})(\\d{2})([A-Z\\d]{2})([A\\d])$/;\n// los genéricos del SAT: público en general (nacional) y extranjero\nconst GENERIC_RFCS = Object.freeze(['XAXX010101000', 'XEXX010101000']);\nconst RFC_CHECK_TABLE = '0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ Ñ';\nconst RFC_CHECK_MODULUS = 11;\nconst RFC_CHECK_LENGTH = 12;\n\n// CURP: 4 letras, la fecha AAMMDD, el sexo (H, M o X), el estado (o NE: nacido en el extranjero), 3 consonantes, el carácter\n// que distingue al siglo y el dígito\nconst CURP_PATTERN = /^[A-Z][AEIOUX][A-Z]{2}(\\d{2})(\\d{2})(\\d{2})[HMX](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)[B-DF-HJ-NP-TV-Z]{3}[A-Z\\d]\\d$/;\nconst CURP_CHECK_TABLE = '0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ';\nconst CURP_BODY_LENGTH = 17;\n\n// CLABE: 18 dígitos; los pesos 3, 7, 1 se repiten sobre los primeros 17\nconst CLABE_PATTERN = /^\\d{18}$/;\nconst CLABE_WEIGHTS = Object.freeze([3, 7, 1]);\nconst DECIMAL_BASE = 10;\n\n/** De qué tipo es un RFC. */\nexport const RFC_KIND = Object.freeze({company: 'company', person: 'person', generic: 'generic'});\n\n/**\n * ¿La fecha AAMMDD existe? (el 31 de febrero no).\n *\n * @param {string} year dos dígitos\n * @param {string} month\n * @param {string} day\n * @returns {boolean}\n */\nfunction isRealShortDate(year, month, day) {\n // el siglo no importa para saber si el día existe, salvo el 29 de febrero: 2000 fue bisiesto y 1900 no, y las dos se\n // escriben «00». Se toma el 2000 (el de cualquier persona viva nacida un 29 de febrero «00»)\n const date = new Date(Date.UTC(2000 + Number(year), Number(month) - 1, Number(day)));\n return date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day);\n}\n\n/**\n * El dígito verificador de un RFC (el último carácter), calculado con el resto de 11 del SAT.\n *\n * @param {string} rfc el RFC completo (se ignora su último carácter)\n * @returns {string} '0'-'9' o 'A'\n */\nexport function rfcCheckDigitOf(rfc) {\n const body = rfc.slice(0, -1).toUpperCase();\n // la persona moral (11 sin el dígito) se rellena a 12 con un espacio al frente\n const padded = body.padStart(RFC_CHECK_LENGTH, ' ');\n let sum = 0;\n for (let position = 0; position < RFC_CHECK_LENGTH; position += 1) {\n sum += RFC_CHECK_TABLE.indexOf(padded[position]) * (RFC_CHECK_LENGTH + 1 - position);\n }\n const rest = sum % RFC_CHECK_MODULUS;\n if (rest === 0) {\n return '0';\n }\n return rest === 1 ? 'A' : String(RFC_CHECK_MODULUS - rest);\n}\n\n/**\n * De qué tipo es un RFC bien formado; null si no tiene la forma.\n *\n * @param {string|null|undefined} rfc\n * @returns {string|null} RFC_KIND\n */\nexport function rfcKindOf(rfc) {\n const clean = (rfc ?? '').trim().toUpperCase();\n if (GENERIC_RFCS.includes(clean)) {\n return RFC_KIND.generic;\n }\n const match = RFC_PATTERN.exec(clean);\n if (!match) {\n return null;\n }\n return match[1].length === 3 ? RFC_KIND.company : RFC_KIND.person;\n}\n\n/**\n * ¿El RFC está bien escrito? Revisa su forma (persona moral, física o genérico) y que la fecha exista.\n *\n * El dígito verificador va aparte (strict) y no por omisión: si un RFC real no lo cumpliera, la persona se quedaría sin poder\n * seguir, y quien dice si un RFC existe es el SAT.\n *\n * @param {string|null|undefined} rfc\n * @param {{strict?: boolean}} [options] strict: además, el dígito verificador\n * @returns {boolean}\n */\nexport function isValidRfc(rfc, {strict = false} = {}) {\n const clean = (rfc ?? '').trim().toUpperCase();\n if (GENERIC_RFCS.includes(clean)) {\n return true;\n }\n const match = RFC_PATTERN.exec(clean);\n if (!match || !isRealShortDate(match[2], match[3], match[4])) {\n return false;\n }\n return !strict || rfcCheckDigitOf(clean) === clean.at(-1);\n}\n\n/**\n * El dígito verificador de una CURP (el último).\n *\n * @param {string} curp la CURP completa (se ignora su último carácter)\n * @returns {string}\n */\nexport function curpCheckDigitOf(curp) {\n const body = curp.slice(0, CURP_BODY_LENGTH).toUpperCase();\n let sum = 0;\n for (let position = 0; position < CURP_BODY_LENGTH; position += 1) {\n sum += CURP_CHECK_TABLE.indexOf(body[position]) * (CURP_BODY_LENGTH + 1 - position);\n }\n return String((DECIMAL_BASE - (sum % DECIMAL_BASE)) % DECIMAL_BASE);\n}\n\n/**\n * ¿La CURP está bien escrita? Su forma (con el estado y el sexo), que la fecha exista y su dígito verificador.\n *\n * @param {string|null|undefined} curp\n * @returns {boolean}\n */\nexport function isValidCurp(curp) {\n const clean = (curp ?? '').trim().toUpperCase();\n const match = CURP_PATTERN.exec(clean);\n return Boolean(match) && isRealShortDate(match[1], match[2], match[3]) && curpCheckDigitOf(clean) === clean.at(-1);\n}\n\n/**\n * El dígito verificador de una CLABE (el 18.º).\n *\n * @param {string} clabe la CLABE completa o sus primeros 17 dígitos\n * @returns {string}\n */\nexport function clabeCheckDigitOf(clabe) {\n let sum = 0;\n for (let position = 0; position < 17; position += 1) {\n sum += (Number(clabe[position]) * CLABE_WEIGHTS[position % CLABE_WEIGHTS.length]) % DECIMAL_BASE;\n }\n return String((DECIMAL_BASE - (sum % DECIMAL_BASE)) % DECIMAL_BASE);\n}\n\n/**\n * ¿La CLABE está bien escrita? 18 dígitos (los espacios no cuentan) y su dígito verificador.\n *\n * @param {string|null|undefined} clabe\n * @returns {boolean}\n */\nexport function isValidClabe(clabe) {\n const clean = (clabe ?? '').replace(/\\s/g, '');\n return CLABE_PATTERN.test(clean) && clabeCheckDigitOf(clean) === clean.at(-1);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mexicoIdentifiers.js","sources":["../../src/mexico/mexicoIdentifiers.js"],"sourcesContent":["// Las claves de México que se escriben en casi cualquier sistema: el RFC (SAT), la CURP (RENAPO) y la CLABE (Banxico). Revisan\n// la forma de cada una y su dígito verificador, para avisar ANTES de mandar algo mal escrito. Lo que de verdad vale (que el\n// RFC exista, que la CLABE sea de esa persona) lo dice el SAT o el banco, del lado del servidor.\n//\n// Son estándares nacionales, no reglas de un cliente.\n//\n// Puro (sin React): se prueba con node (mexicoIdentifiers.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n// RFC: 3 letras (persona moral) o 4 (persona física), la fecha AAMMDD y la homoclave de 3 (la última es el dígito)\nconst RFC_PATTERN = /^([A-ZÑ&]{3,4})(\\d{2})(\\d{2})(\\d{2})([A-Z\\d]{2})([A\\d])$/;\n// los genéricos del SAT: público en general (nacional) y extranjero\nconst GENERIC_RFCS = Object.freeze(['XAXX010101000', 'XEXX010101000']);\nconst RFC_CHECK_TABLE = '0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ Ñ';\nconst RFC_CHECK_MODULUS = 11;\nconst RFC_CHECK_LENGTH = 12;\n\n// CURP: 4 letras, la fecha AAMMDD, el sexo (H, M o X), el estado (o NE: nacido en el extranjero), 3 consonantes, el carácter\n// que distingue al siglo y el dígito\nconst CURP_PATTERN = /^[A-Z][AEIOUX][A-Z]{2}(\\d{2})(\\d{2})(\\d{2})[HMX](AS|BC|BS|CC|CL|CM|CS|CH|DF|DG|GT|GR|HG|JC|MC|MN|MS|NT|NL|OC|PL|QT|QR|SP|SL|SR|TC|TS|TL|VZ|YN|ZS|NE)[B-DF-HJ-NP-TV-Z]{3}[A-Z\\d]\\d$/;\nconst CURP_CHECK_TABLE = '0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ';\nconst CURP_BODY_LENGTH = 17;\n\n// CLABE: 18 dígitos; los pesos 3, 7, 1 se repiten sobre los primeros 17\nconst CLABE_PATTERN = /^\\d{18}$/;\nconst CLABE_WEIGHTS = Object.freeze([3, 7, 1]);\nconst DECIMAL_BASE = 10;\n\n/** De qué tipo es un RFC. */\nexport const RFC_KIND = Object.freeze({company: 'company', person: 'person', generic: 'generic'});\n\n/**\n * ¿La fecha AAMMDD existe? (el 31 de febrero no).\n *\n * @param {string} year dos dígitos\n * @param {string} month\n * @param {string} day\n * @returns {boolean}\n */\nfunction isRealShortDate(year, month, day) {\n // el siglo no importa para saber si el día existe, salvo el 29 de febrero: 2000 fue bisiesto y 1900 no, y las dos se\n // escriben «00». Se toma el 2000 (el de cualquier persona viva nacida un 29 de febrero «00»)\n const date = new Date(Date.UTC(2000 + Number(year), Number(month) - 1, Number(day)));\n return date.getUTCMonth() === Number(month) - 1 && date.getUTCDate() === Number(day);\n}\n\n/**\n * El dígito verificador de un RFC (el último carácter), calculado con el resto de 11 del SAT.\n *\n * @param {string} rfc el RFC completo (se ignora su último carácter)\n * @returns {string} '0'-'9' o 'A'\n */\nexport function rfcCheckDigitOf(rfc) {\n const body = rfc.slice(0, -1).toUpperCase();\n // la persona moral (11 sin el dígito) se rellena a 12 con un espacio al frente\n const padded = body.padStart(RFC_CHECK_LENGTH, ' ');\n let sum = 0;\n for (let position = 0; position < RFC_CHECK_LENGTH; position += 1) {\n sum += RFC_CHECK_TABLE.indexOf(padded[position]) * (RFC_CHECK_LENGTH + 1 - position);\n }\n const rest = sum % RFC_CHECK_MODULUS;\n if (rest === 0) {\n return '0';\n }\n return rest === 1 ? 'A' : String(RFC_CHECK_MODULUS - rest);\n}\n\n/**\n * De qué tipo es un RFC bien formado; null si no tiene la forma.\n *\n * @param {string|null|undefined} rfc\n * @returns {string|null} RFC_KIND\n */\nexport function rfcKindOf(rfc) {\n const clean = (rfc ?? '').trim().toUpperCase();\n if (GENERIC_RFCS.includes(clean)) {\n return RFC_KIND.generic;\n }\n const match = RFC_PATTERN.exec(clean);\n if (!match) {\n return null;\n }\n return match[1].length === 3 ? RFC_KIND.company : RFC_KIND.person;\n}\n\n/**\n * ¿El RFC está bien escrito? Revisa su forma (persona moral, física o genérico) y que la fecha exista.\n *\n * El dígito verificador va aparte (strict) y no por omisión: si un RFC real no lo cumpliera, la persona se quedaría sin poder\n * seguir, y quien dice si un RFC existe es el SAT.\n *\n * @param {string|null|undefined} rfc\n * @param {{strict?: boolean}} [options] strict: además, el dígito verificador\n * @returns {boolean}\n */\nexport function isValidRfc(rfc, {strict = false} = {}) {\n const clean = (rfc ?? '').trim().toUpperCase();\n if (GENERIC_RFCS.includes(clean)) {\n return true;\n }\n const match = RFC_PATTERN.exec(clean);\n if (!match || !isRealShortDate(match[2], match[3], match[4])) {\n return false;\n }\n return !strict || rfcCheckDigitOf(clean) === clean.at(-1);\n}\n\n/**\n * El dígito verificador de una CURP (el último).\n *\n * @param {string} curp la CURP completa (se ignora su último carácter)\n * @returns {string}\n */\nexport function curpCheckDigitOf(curp) {\n const body = curp.slice(0, CURP_BODY_LENGTH).toUpperCase();\n let sum = 0;\n for (let position = 0; position < CURP_BODY_LENGTH; position += 1) {\n sum += CURP_CHECK_TABLE.indexOf(body[position]) * (CURP_BODY_LENGTH + 1 - position);\n }\n return String((DECIMAL_BASE - (sum % DECIMAL_BASE)) % DECIMAL_BASE);\n}\n\n/**\n * ¿La CURP está bien escrita? Su forma (con el estado y el sexo), que la fecha exista y su dígito verificador.\n *\n * @param {string|null|undefined} curp\n * @returns {boolean}\n */\nexport function isValidCurp(curp) {\n const clean = (curp ?? '').trim().toUpperCase();\n const match = CURP_PATTERN.exec(clean);\n return Boolean(match) && isRealShortDate(match[1], match[2], match[3]) && curpCheckDigitOf(clean) === clean.at(-1);\n}\n\n/**\n * El dígito verificador de una CLABE (el 18.º).\n *\n * @param {string} clabe la CLABE completa o sus primeros 17 dígitos\n * @returns {string}\n */\nexport function clabeCheckDigitOf(clabe) {\n let sum = 0;\n for (let position = 0; position < 17; position += 1) {\n sum += (Number(clabe[position]) * CLABE_WEIGHTS[position % CLABE_WEIGHTS.length]) % DECIMAL_BASE;\n }\n return String((DECIMAL_BASE - (sum % DECIMAL_BASE)) % DECIMAL_BASE);\n}\n\n/**\n * ¿La CLABE está bien escrita? 18 dígitos (los espacios no cuentan) y su dígito verificador.\n *\n * @param {string|null|undefined} clabe\n * @returns {boolean}\n */\nexport function isValidClabe(clabe) {\n const clean = (clabe ?? '').replace(/\\s/g, '');\n return CLABE_PATTERN.test(clean) && clabeCheckDigitOf(clean) === clean.at(-1);\n}\n\n// el código postal de México: 5 dígitos; no hay códigos que empiecen con 00 (los estados van del 01 al 99)\nconst POSTAL_CODE_PATTERN = /^(?!00)\\d{5}$/;\n\n/**\n * ¿El código postal está bien escrito? 5 dígitos (los espacios no cuentan) que no empiezan con 00.\n *\n * @param {string|null|undefined} postalCode\n * @returns {boolean}\n */\nexport function isValidPostalCode(postalCode) {\n return POSTAL_CODE_PATTERN.test((postalCode ?? '').replace(/\\s/g, ''));\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ruleMessageOf } from "../forms/validationMessages.js";
|
|
2
|
+
import { isValidClabe, isValidCurp, isValidPostalCode, isValidRfc } from "./mexicoIdentifiers.js";
|
|
2
3
|
function rfcRule({ strict = false } = {}) {
|
|
3
|
-
return { name: "rfc", message:
|
|
4
|
+
return { name: "rfc", message: ruleMessageOf("validation:rfc", "rfc"), test: (value) => !value || isValidRfc(String(value), { strict }) };
|
|
4
5
|
}
|
|
5
6
|
function curpRule() {
|
|
6
|
-
return { name: "curp", message:
|
|
7
|
+
return { name: "curp", message: ruleMessageOf("validation:curp", "curp"), test: (value) => !value || isValidCurp(String(value)) };
|
|
7
8
|
}
|
|
8
9
|
function clabeRule() {
|
|
9
|
-
return { name: "clabe", message:
|
|
10
|
+
return { name: "clabe", message: ruleMessageOf("validation:clabe", "clabe"), test: (value) => !value || isValidClabe(String(value)) };
|
|
11
|
+
}
|
|
12
|
+
function postalCodeRule() {
|
|
13
|
+
return { name: "postalCode", message: ruleMessageOf("validation:postalCode", "postalCode"), test: (value) => !value || isValidPostalCode(String(value)) };
|
|
10
14
|
}
|
|
11
15
|
export {
|
|
12
16
|
clabeRule,
|
|
13
17
|
curpRule,
|
|
18
|
+
postalCodeRule,
|
|
14
19
|
rfcRule
|
|
15
20
|
};
|
|
16
21
|
//# sourceMappingURL=mexicoRules.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mexicoRules.js","sources":["../../src/mexico/mexicoRules.js"],"sourcesContent":["// Las reglas de yup para el RFC, la CURP y
|
|
1
|
+
{"version":3,"file":"mexicoRules.js","sources":["../../src/mexico/mexicoRules.js"],"sourcesContent":["// Las reglas de yup para el RFC, la CURP, la CLABE y el código postal, con sus mensajes en el idioma activo (validation:rfc,\n// validation:curp, validation:clabe, validation:postalCode). Un campo vacío pasa: que sea obligatorio lo dice required().\n// Cada mensaje lleva su regla: la pantalla (messages del campo) o los textos del proyecto pueden poner el suyo sin tocar el esquema.\n//\n// Yup.object({rfc: Yup.string().required().test(rfcRule()), clabe: Yup.string().test(clabeRule())})\n//\n// Son objetos de .test() y no un Yup propio: el proyecto usa su copia de yup y aquí no se importa.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {ruleMessageOf} from '@/forms/validationMessages.js';\nimport {isValidClabe, isValidCurp, isValidPostalCode, isValidRfc} from '@/mexico/mexicoIdentifiers.js';\n\n/**\n * @param {{strict?: boolean}} [options] strict: también el dígito verificador (ver isValidRfc)\n * @returns {{name: string, message: {key: string, values: object, rule: string}, test: (value: unknown) => boolean}}\n */\nexport function rfcRule({strict = false} = {}) {\n return {name: 'rfc', message: ruleMessageOf('validation:rfc', 'rfc'), test: (value) => !value || isValidRfc(String(value), {strict})};\n}\n\n/** @returns {{name: string, message: {key: string, values: object, rule: string}, test: (value: unknown) => boolean}} */\nexport function curpRule() {\n return {name: 'curp', message: ruleMessageOf('validation:curp', 'curp'), test: (value) => !value || isValidCurp(String(value))};\n}\n\n/** @returns {{name: string, message: {key: string, values: object, rule: string}, test: (value: unknown) => boolean}} */\nexport function clabeRule() {\n return {name: 'clabe', message: ruleMessageOf('validation:clabe', 'clabe'), test: (value) => !value || isValidClabe(String(value))};\n}\n\n/** @returns {{name: string, message: {key: string, values: object, rule: string}, test: (value: unknown) => boolean}} */\nexport function postalCodeRule() {\n return {name: 'postalCode', message: ruleMessageOf('validation:postalCode', 'postalCode'), test: (value) => !value || isValidPostalCode(String(value))};\n}\n"],"names":[],"mappings":";;AAgBO,SAAS,QAAQ,EAAC,SAAS,MAAK,IAAI,CAAA,GAAI;AAC3C,SAAO,EAAC,MAAM,OAAO,SAAS,cAAc,kBAAkB,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,WAAW,OAAO,KAAK,GAAG,EAAC,OAAM,CAAC,EAAC;AACxI;AAGO,SAAS,WAAW;AACvB,SAAO,EAAC,MAAM,QAAQ,SAAS,cAAc,mBAAmB,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,YAAY,OAAO,KAAK,CAAC,EAAC;AAClI;AAGO,SAAS,YAAY;AACxB,SAAO,EAAC,MAAM,SAAS,SAAS,cAAc,oBAAoB,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,aAAa,OAAO,KAAK,CAAC,EAAC;AACtI;AAGO,SAAS,iBAAiB;AAC7B,SAAO,EAAC,MAAM,cAAc,SAAS,cAAc,yBAAyB,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,kBAAkB,OAAO,KAAK,CAAC,EAAC;AAC1J;"}
|
package/dist/mexico.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RFC_KIND, clabeCheckDigitOf, curpCheckDigitOf, isValidClabe, isValidCurp, isValidRfc, rfcCheckDigitOf, rfcKindOf } from "./mexico/mexicoIdentifiers.js";
|
|
2
|
-
import { clabeRule, curpRule, rfcRule } from "./mexico/mexicoRules.js";
|
|
1
|
+
import { RFC_KIND, clabeCheckDigitOf, curpCheckDigitOf, isValidClabe, isValidCurp, isValidPostalCode, isValidRfc, rfcCheckDigitOf, rfcKindOf } from "./mexico/mexicoIdentifiers.js";
|
|
2
|
+
import { clabeRule, curpRule, postalCodeRule, rfcRule } from "./mexico/mexicoRules.js";
|
|
3
3
|
import { CERTIFICATE_STATUS, certificateStatusOf, readCertificate } from "./mexico/certificateReader.js";
|
|
4
4
|
import { CFDI_VOUCHER_TYPE, readCfdi, readCfdiFile } from "./mexico/cfdiReader.js";
|
|
5
5
|
import { EFIRMA_KEY_PROBLEM, EfirmaKeyError, verifyEfirmaKey } from "./mexico/efirmaKey.js";
|
|
@@ -20,7 +20,9 @@ export {
|
|
|
20
20
|
curpRule,
|
|
21
21
|
isValidClabe,
|
|
22
22
|
isValidCurp,
|
|
23
|
+
isValidPostalCode,
|
|
23
24
|
isValidRfc,
|
|
25
|
+
postalCodeRule,
|
|
24
26
|
readCertificate,
|
|
25
27
|
readCfdi,
|
|
26
28
|
readCfdiFile,
|
|
@@ -9,11 +9,11 @@ import { INSTALL_VARIANT } from "./installInstructions.js";
|
|
|
9
9
|
import InstallInstructionsDialog from "./InstallInstructionsDialog.js";
|
|
10
10
|
import { useInstallPrompt } from "./useInstallPrompt.js";
|
|
11
11
|
import ComponentTexts from "../toolkit/ComponentTexts.js";
|
|
12
|
-
import { piecePart } from "../theme/themePieces.js";
|
|
12
|
+
import { piecePart, withPieceSlots } from "../theme/themePieces.js";
|
|
13
13
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
14
14
|
const THEME_NAME = "CalcitaInstallAppSetting";
|
|
15
15
|
const InstallRoot = piecePart(THEME_NAME, "Root", Box);
|
|
16
|
-
function
|
|
16
|
+
function InstallAppSettingContent({ texts, ...unknownProps } = {}) {
|
|
17
17
|
warnUnknownProps("InstallAppSetting", unknownProps);
|
|
18
18
|
const { t } = useToolkitTranslation("common", texts);
|
|
19
19
|
const { variant, promptInstall } = useInstallPrompt();
|
|
@@ -33,6 +33,7 @@ function InstallAppSetting({ texts, ...unknownProps } = {}) {
|
|
|
33
33
|
/* @__PURE__ */ jsx(InstallInstructionsDialog, { variant, open: stepsOpen, onClose: () => setStepsOpen(false) })
|
|
34
34
|
] }) });
|
|
35
35
|
}
|
|
36
|
+
const InstallAppSetting = withPieceSlots(InstallAppSettingContent, THEME_NAME);
|
|
36
37
|
export {
|
|
37
38
|
InstallAppSetting as default
|
|
38
39
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallAppSetting.js","sources":["../../src/pwa/InstallAppSetting.jsx"],"sourcesContent":["// «Instalar la app» dentro del menú de la cuenta (accountSettings de AppLayout): para quien dijo «Ahora no» a la invitación\n// y después sí la quiere. No respeta el «Ahora no» (aquí la persona la busca a propósito). Ya instalada o sin manera de\n// instalar en ese equipo, no pinta nada.\n//\n// El diálogo de los pasos va aquí mismo: el menú de la cuenta se queda abierto mientras se ve, y al cerrarlo se regresa al\n// menú.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaInstallAppSetting con defaultProps y\n// styleOverrides de sus partes: root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport InstallMobileRoundedIcon from '@mui/icons-material/InstallMobileRounded';\n\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {INSTALL_VARIANT} from '@/pwa/installInstructions.js';\nimport InstallInstructionsDialog from '@/pwa/InstallInstructionsDialog.jsx';\nimport {useInstallPrompt} from '@/pwa/useInstallPrompt.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart} 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.CalcitaInstallAppSetting)\nconst THEME_NAME = 'CalcitaInstallAppSetting';\nconst InstallRoot = piecePart(THEME_NAME, 'Root', Box);\n\n/**\n * @param {object} [props]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"InstallAppSetting.js","sources":["../../src/pwa/InstallAppSetting.jsx"],"sourcesContent":["// «Instalar la app» dentro del menú de la cuenta (accountSettings de AppLayout): para quien dijo «Ahora no» a la invitación\n// y después sí la quiere. No respeta el «Ahora no» (aquí la persona la busca a propósito). Ya instalada o sin manera de\n// instalar en ese equipo, no pinta nada.\n//\n// El diálogo de los pasos va aquí mismo: el menú de la cuenta se queda abierto mientras se ve, y al cerrarlo se regresa al\n// menú.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaInstallAppSetting con defaultProps y\n// styleOverrides de sus partes: root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport InstallMobileRoundedIcon from '@mui/icons-material/InstallMobileRounded';\n\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {INSTALL_VARIANT} from '@/pwa/installInstructions.js';\nimport InstallInstructionsDialog from '@/pwa/InstallInstructionsDialog.jsx';\nimport {useInstallPrompt} from '@/pwa/useInstallPrompt.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart, 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.CalcitaInstallAppSetting)\nconst THEME_NAME = 'CalcitaInstallAppSetting';\nconst InstallRoot = piecePart(THEME_NAME, 'Root', Box);\n\n/**\n * @param {object} [props]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction InstallAppSettingContent({texts, ...unknownProps} = {}) {\n warnUnknownProps('InstallAppSetting', unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const {variant, promptInstall} = useInstallPrompt();\n const [stepsOpen, setStepsOpen] = useState(false);\n\n if (!variant) {\n return null;\n }\n\n const handleClick = () => {\n if (variant === INSTALL_VARIANT.native) {\n promptInstall();\n } else {\n setStepsOpen(true);\n }\n };\n\n return (\n <ComponentTexts namespace=\"common\" texts={texts}>\n <InstallRoot ownSx={{px: 1, py: 0.5}}>\n <Button fullWidth variant=\"outlined\" startIcon={<InstallMobileRoundedIcon />} onClick={handleClick}>\n {t('install.menuLabel')}\n </Button>\n <InstallInstructionsDialog variant={variant} open={stepsOpen} onClose={() => setStepsOpen(false)} />\n </InstallRoot>\n </ComponentTexts>\n );\n}\n\nconst InstallAppSetting = withPieceSlots(InstallAppSettingContent, THEME_NAME);\nexport default InstallAppSetting;\n"],"names":[],"mappings":";;;;;;;;;;;;;AAyBA,MAAM,aAAa;AACnB,MAAM,cAAc,UAAU,YAAY,QAAQ,GAAG;AAMrD,SAAS,yBAAyB,EAAC,OAAO,GAAG,aAAA,IAAgB,CAAA,GAAI;AAC7D,mBAAiB,qBAAqB,YAAY;AAClD,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,SAAS,cAAA,IAAiB,iBAAA;AACjC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,MAAI,CAAC,SAAS;AACV,WAAO;AAAA,EACX;AAEA,QAAM,cAAc,MAAM;AACtB,QAAI,YAAY,gBAAgB,QAAQ;AACpC,oBAAA;AAAA,IACJ,OAAO;AACH,mBAAa,IAAI;AAAA,IACrB;AAAA,EACJ;AAEA,SACI,oBAAC,gBAAA,EAAe,WAAU,UAAS,OAC/B,UAAA,qBAAC,aAAA,EAAY,OAAO,EAAC,IAAI,GAAG,IAAI,OAC5B,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAO,WAAS,MAAC,SAAQ,YAAW,WAAW,oBAAC,0BAAA,CAAA,CAAyB,GAAI,SAAS,aAClF,UAAA,EAAE,mBAAmB,GAC1B;AAAA,IACA,oBAAC,6BAA0B,SAAkB,MAAM,WAAW,SAAS,MAAM,aAAa,KAAK,EAAA,CAAG;AAAA,EAAA,EAAA,CACtG,EAAA,CACJ;AAER;AAEA,MAAM,oBAAoB,eAAe,0BAA0B,UAAU;"}
|
|
@@ -8,7 +8,7 @@ import DialogTitle from "@mui/material/DialogTitle";
|
|
|
8
8
|
import Typography from "@mui/material/Typography";
|
|
9
9
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
10
10
|
import { INSTALL_VARIANT } from "./installInstructions.js";
|
|
11
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
11
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
12
12
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
13
13
|
const THEME_NAME = "CalcitaInstallInstructions";
|
|
14
14
|
const InstructionsSteps = piecePart(THEME_NAME, "Steps", Typography);
|
|
@@ -20,7 +20,7 @@ const STEPS_BY_VARIANT = Object.freeze({
|
|
|
20
20
|
function variantHasInstructions(variant) {
|
|
21
21
|
return Boolean(STEPS_BY_VARIANT[variant]);
|
|
22
22
|
}
|
|
23
|
-
function
|
|
23
|
+
function InstallInstructionsDialogContent(props) {
|
|
24
24
|
const { variant, open, onClose, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
25
25
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
26
26
|
const { t } = useToolkitTranslation("common", texts);
|
|
@@ -34,6 +34,7 @@ function InstallInstructionsDialog(props) {
|
|
|
34
34
|
/* @__PURE__ */ jsx(DialogActions, { children: /* @__PURE__ */ jsx(Button, { variant: "contained", onClick: onClose, children: t("install.gotIt") }) })
|
|
35
35
|
] });
|
|
36
36
|
}
|
|
37
|
+
const InstallInstructionsDialog = withPieceSlots(InstallInstructionsDialogContent, THEME_NAME);
|
|
37
38
|
export {
|
|
38
39
|
InstallInstructionsDialog as default,
|
|
39
40
|
variantHasInstructions
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallInstructionsDialog.js","sources":["../../src/pwa/InstallInstructionsDialog.jsx"],"sourcesContent":["// Los pasos para instalar la app a mano, según el equipo (iOS 26 cambió el camino; Firefox usa su propio menú). Lo usan la\n// invitación (InstallPromptBanner) y la entrada del menú de la cuenta (InstallAppSetting): los dos abren el mismo diálogo.\n// Si la variante no lleva pasos (el navegador tiene su propio diálogo, o no hay nada que ofrecer) no pinta nada.\n//\n// OJO, para quien lo monte: NO dentro de un Menu de MUI que se cierra; el Menu quita a sus hijos al cerrarse y el diálogo\n// nunca aparecería. Va junto al Menu o en un lugar que se quede pintado.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaInstallInstructions con defaultProps y\n// styleOverrides de sus partes: steps.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Typography from '@mui/material/Typography';\n\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {INSTALL_VARIANT} from '@/pwa/installInstructions.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.CalcitaInstallInstructions)\nconst THEME_NAME = 'CalcitaInstallInstructions';\nconst InstructionsSteps = piecePart(THEME_NAME, 'Steps', Typography);\n\n// cada variante con pasos a mano → su título y sus pasos en los textos del paquete (common:install.<variante>)\nconst STEPS_BY_VARIANT = Object.freeze({\n [INSTALL_VARIANT.iosModern]: ['step1', 'step2', 'step3', 'step4'],\n [INSTALL_VARIANT.iosClassic]: ['step1', 'step2', 'step3'],\n [INSTALL_VARIANT.androidFirefox]: ['step1', 'step2', 'step3'],\n});\n\n/**\n * ¿Esta variante se instala con pasos a mano (y no con el diálogo del navegador)?\n *\n * @param {string|null} variant\n * @returns {boolean}\n */\nexport function variantHasInstructions(variant) {\n return Boolean(STEPS_BY_VARIANT[variant]);\n}\n\n/**\n * @param {object} props\n * @param {string|null} props.variant una de INSTALL_VARIANT (useInstallPrompt)\n * @param {boolean} props.open\n * @param {() => void} props.onClose\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"InstallInstructionsDialog.js","sources":["../../src/pwa/InstallInstructionsDialog.jsx"],"sourcesContent":["// Los pasos para instalar la app a mano, según el equipo (iOS 26 cambió el camino; Firefox usa su propio menú). Lo usan la\n// invitación (InstallPromptBanner) y la entrada del menú de la cuenta (InstallAppSetting): los dos abren el mismo diálogo.\n// Si la variante no lleva pasos (el navegador tiene su propio diálogo, o no hay nada que ofrecer) no pinta nada.\n//\n// OJO, para quien lo monte: NO dentro de un Menu de MUI que se cierra; el Menu quita a sus hijos al cerrarse y el diálogo\n// nunca aparecería. Va junto al Menu o en un lugar que se quede pintado.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaInstallInstructions con defaultProps y\n// styleOverrides de sus partes: steps.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Typography from '@mui/material/Typography';\n\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {INSTALL_VARIANT} from '@/pwa/installInstructions.js';\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.CalcitaInstallInstructions)\nconst THEME_NAME = 'CalcitaInstallInstructions';\nconst InstructionsSteps = piecePart(THEME_NAME, 'Steps', Typography);\n\n// cada variante con pasos a mano → su título y sus pasos en los textos del paquete (common:install.<variante>)\nconst STEPS_BY_VARIANT = Object.freeze({\n [INSTALL_VARIANT.iosModern]: ['step1', 'step2', 'step3', 'step4'],\n [INSTALL_VARIANT.iosClassic]: ['step1', 'step2', 'step3'],\n [INSTALL_VARIANT.androidFirefox]: ['step1', 'step2', 'step3'],\n});\n\n/**\n * ¿Esta variante se instala con pasos a mano (y no con el diálogo del navegador)?\n *\n * @param {string|null} variant\n * @returns {boolean}\n */\nexport function variantHasInstructions(variant) {\n return Boolean(STEPS_BY_VARIANT[variant]);\n}\n\n/**\n * @param {object} props\n * @param {string|null} props.variant una de INSTALL_VARIANT (useInstallPrompt)\n * @param {boolean} props.open\n * @param {() => void} props.onClose\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction InstallInstructionsDialogContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaInstallInstructions.defaultProps) y encima lo que llega aquí\n const {variant, open, onClose, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const steps = STEPS_BY_VARIANT[variant];\n if (!steps) {\n return null;\n }\n\n return (\n <Dialog open={open} onClose={onClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle>{t(`install.${variant}.title`)}</DialogTitle>\n <DialogContent>\n {/* pasos numerados con palabras, sin dibujar los íconos del sistema: cambian entre versiones */}\n <InstructionsSteps component=\"ol\" ownSx={{m: 0, pl: 3, display: 'grid', gap: 1.25}}>\n {steps.map((step) => (\n <Typography key={step} component=\"li\" variant=\"body1\">{t(`install.${variant}.${step}`)}</Typography>\n ))}\n </InstructionsSteps>\n </DialogContent>\n <DialogActions>\n <Button variant=\"contained\" onClick={onClose}>{t('install.gotIt')}</Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst InstallInstructionsDialog = withPieceSlots(InstallInstructionsDialogContent, THEME_NAME);\nexport default InstallInstructionsDialog;\n"],"names":[],"mappings":";;;;;;;;;;;;AAwBA,MAAM,aAAa;AACnB,MAAM,oBAAoB,UAAU,YAAY,SAAS,UAAU;AAGnE,MAAM,mBAAmB,OAAO,OAAO;AAAA,EACnC,CAAC,gBAAgB,SAAS,GAAG,CAAC,SAAS,SAAS,SAAS,OAAO;AAAA,EAChE,CAAC,gBAAgB,UAAU,GAAG,CAAC,SAAS,SAAS,OAAO;AAAA,EACxD,CAAC,gBAAgB,cAAc,GAAG,CAAC,SAAS,SAAS,OAAO;AAChE,CAAC;AAQM,SAAS,uBAAuB,SAAS;AAC5C,SAAO,QAAQ,iBAAiB,OAAO,CAAC;AAC5C;AASA,SAAS,iCAAiC,OAAO;AAE7C,QAAM,EAAC,SAAS,MAAM,SAAS,OAAO,GAAG,iBAAgB,cAAc,OAAO,UAAU;AACxF,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,QAAQ,iBAAiB,OAAO;AACtC,MAAI,CAAC,OAAO;AACR,WAAO;AAAA,EACX;AAEA,8BACK,QAAA,EAAO,MAAY,SAAkB,UAAS,MAAK,WAAS,MACzD,UAAA;AAAA,IAAA,oBAAC,aAAA,EAAa,UAAA,EAAE,WAAW,OAAO,QAAQ,GAAE;AAAA,wBAC3C,eAAA,EAEG,UAAA,oBAAC,mBAAA,EAAkB,WAAU,MAAK,OAAO,EAAC,GAAG,GAAG,IAAI,GAAG,SAAS,QAAQ,KAAK,QACxE,UAAA,MAAM,IAAI,CAAC,SACR,oBAAC,YAAA,EAAsB,WAAU,MAAK,SAAQ,SAAS,UAAA,EAAE,WAAW,OAAO,IAAI,IAAI,EAAE,KAApE,IAAsE,CAC1F,GACL,GACJ;AAAA,IACA,oBAAC,eAAA,EACG,UAAA,oBAAC,QAAA,EAAO,SAAQ,aAAY,SAAS,SAAU,UAAA,EAAE,eAAe,EAAA,CAAE,EAAA,CACtE;AAAA,EAAA,GACJ;AAER;AAGA,MAAM,4BAA4B,eAAe,kCAAkC,UAAU;"}
|
|
@@ -14,13 +14,13 @@ import { INSTALL_VARIANT } from "./installInstructions.js";
|
|
|
14
14
|
import InstallInstructionsDialog from "./InstallInstructionsDialog.js";
|
|
15
15
|
import { useInstallPrompt } from "./useInstallPrompt.js";
|
|
16
16
|
import ComponentTexts from "../toolkit/ComponentTexts.js";
|
|
17
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
17
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
18
18
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
19
19
|
const THEME_NAME = "CalcitaInstallPromptBanner";
|
|
20
20
|
const PromptRoot = piecePart(THEME_NAME, "Root", Snackbar);
|
|
21
21
|
const PromptAlert = piecePart(THEME_NAME, "Alert", Alert);
|
|
22
22
|
const TAB_BAR_GAP_PIXELS = 8;
|
|
23
|
-
function
|
|
23
|
+
function InstallPromptBannerContent(props) {
|
|
24
24
|
const { message, onInstallChoice, onShowSteps, onDismiss, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
25
25
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
26
26
|
const { t } = useToolkitTranslation("common", texts);
|
|
@@ -71,6 +71,7 @@ function InstallPromptBanner(props) {
|
|
|
71
71
|
/* @__PURE__ */ jsx(InstallInstructionsDialog, { variant, open: stepsOpen, onClose: handleStepsClose })
|
|
72
72
|
] }) });
|
|
73
73
|
}
|
|
74
|
+
const InstallPromptBanner = withPieceSlots(InstallPromptBannerContent, THEME_NAME);
|
|
74
75
|
export {
|
|
75
76
|
InstallPromptBanner as default
|
|
76
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallPromptBanner.js","sources":["../../src/pwa/InstallPromptBanner.jsx"],"sourcesContent":["// La invitación a instalar la app, abajo al centro. Según el equipo (useInstallPrompt): con el diálogo del navegador, el\n// botón «Instalar»; en iPhone, iPad y Firefox de Android, «Cómo instalar» abre los pasos (InstallInstructionsDialog).\n// Ya instalada, sin manera de instalar o después de «Ahora no», no pinta nada; quien se arrepienta la encuentra en el menú\n// de la cuenta (InstallAppSetting).\n//\n// Color de información, no de advertencia: instalar es una sugerencia, no un problema. En el teléfono con vidrio va encima\n// de la barra de pestañas; sin ella, respeta la zona del gesto de inicio del iPhone.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaInstallPromptBanner con defaultProps y\n// styleOverrides de sus partes: root, alert.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport Snackbar from '@mui/material/Snackbar';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\nimport InstallMobileRoundedIcon from '@mui/icons-material/InstallMobileRounded';\n\nimport {aboveMobileTabBarOf} from '@/appShell/shellSizes.js';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {INSTALL_VARIANT} from '@/pwa/installInstructions.js';\nimport InstallInstructionsDialog from '@/pwa/InstallInstructionsDialog.jsx';\nimport {useInstallPrompt} from '@/pwa/useInstallPrompt.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.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.CalcitaInstallPromptBanner)\nconst THEME_NAME = 'CalcitaInstallPromptBanner';\nconst PromptRoot = piecePart(THEME_NAME, 'Root', Snackbar);\nconst PromptAlert = piecePart(THEME_NAME, 'Alert', Alert);\n\n// lo que se separa de la barra de pestañas (px)\nconst TAB_BAR_GAP_PIXELS = 8;\n\n/**\n * @param {object} [props]\n * @param {string} [props.message] ya traducido; por omisión el del paquete con el nombre de la app (appName de ToolkitProvider)\n * @param {(outcome: 'accepted'|'dismissed'|null) => void} [props.onInstallChoice] lo que contestó la persona al diálogo de\n * instalar del navegador (Chrome, Edge, Android)\n * @param {() => void} [props.onShowSteps] abrió los pasos para instalar (iPhone, iPad, Firefox de Android)\n * @param {() => void} [props.onDismiss] dijo «Ahora no» (o cerró los pasos): no se le vuelve a ofrecer en este navegador\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"InstallPromptBanner.js","sources":["../../src/pwa/InstallPromptBanner.jsx"],"sourcesContent":["// La invitación a instalar la app, abajo al centro. Según el equipo (useInstallPrompt): con el diálogo del navegador, el\n// botón «Instalar»; en iPhone, iPad y Firefox de Android, «Cómo instalar» abre los pasos (InstallInstructionsDialog).\n// Ya instalada, sin manera de instalar o después de «Ahora no», no pinta nada; quien se arrepienta la encuentra en el menú\n// de la cuenta (InstallAppSetting).\n//\n// Color de información, no de advertencia: instalar es una sugerencia, no un problema. En el teléfono con vidrio va encima\n// de la barra de pestañas; sin ella, respeta la zona del gesto de inicio del iPhone.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaInstallPromptBanner con defaultProps y\n// styleOverrides de sus partes: root, alert.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport Snackbar from '@mui/material/Snackbar';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\nimport InstallMobileRoundedIcon from '@mui/icons-material/InstallMobileRounded';\n\nimport {aboveMobileTabBarOf} from '@/appShell/shellSizes.js';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {INSTALL_VARIANT} from '@/pwa/installInstructions.js';\nimport InstallInstructionsDialog from '@/pwa/InstallInstructionsDialog.jsx';\nimport {useInstallPrompt} from '@/pwa/useInstallPrompt.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaInstallPromptBanner)\nconst THEME_NAME = 'CalcitaInstallPromptBanner';\nconst PromptRoot = piecePart(THEME_NAME, 'Root', Snackbar);\nconst PromptAlert = piecePart(THEME_NAME, 'Alert', Alert);\n\n// lo que se separa de la barra de pestañas (px)\nconst TAB_BAR_GAP_PIXELS = 8;\n\n/**\n * @param {object} [props]\n * @param {string} [props.message] ya traducido; por omisión el del paquete con el nombre de la app (appName de ToolkitProvider)\n * @param {(outcome: 'accepted'|'dismissed'|null) => void} [props.onInstallChoice] lo que contestó la persona al diálogo de\n * instalar del navegador (Chrome, Edge, Android)\n * @param {() => void} [props.onShowSteps] abrió los pasos para instalar (iPhone, iPad, Firefox de Android)\n * @param {() => void} [props.onDismiss] dijo «Ahora no» (o cerró los pasos): no se le vuelve a ofrecer en este navegador\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction InstallPromptBannerContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaInstallPromptBanner.defaultProps) y encima lo que llega aquí\n const {message, onInstallChoice, onShowSteps, onDismiss, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const {variant, dismissed, promptInstall, dismiss} = useInstallPrompt();\n const [stepsOpen, setStepsOpen] = useState(false);\n\n // nada que ofrecer o ya dijo «Ahora no»\n if (!variant || dismissed) {\n return null;\n }\n\n const isNative = variant === INSTALL_VARIANT.native;\n\n const handleAction = async () => {\n if (isNative) {\n onInstallChoice?.(await promptInstall());\n } else {\n setStepsOpen(true);\n onShowSteps?.();\n }\n };\n const handleDismiss = () => {\n dismiss();\n onDismiss?.();\n };\n\n // si ya vio los pasos, no se le vuelven a ofrecer en cada visita\n const handleStepsClose = () => {\n setStepsOpen(false);\n handleDismiss();\n };\n\n return (\n <ComponentTexts namespace=\"common\" texts={texts}>\n <>\n <PromptRoot open anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}\n ownSx={(theme) => (usesMobileTabBar(theme)\n ? aboveMobileTabBarOf(TAB_BAR_GAP_PIXELS)\n : {mb: 'env(safe-area-inset-bottom)'})}>\n <PromptAlert severity=\"info\" icon={<InstallMobileRoundedIcon />} ownSx={{width: '100%', alignItems: 'center'}}\n action={(\n <>\n <Button color=\"inherit\" size=\"small\" onClick={handleAction}>\n {isNative ? t('install.action') : t('install.howTo')}\n </Button>\n <IconButton size=\"small\" color=\"inherit\" aria-label={t('install.dismiss')} onClick={handleDismiss}>\n <CloseRoundedIcon fontSize=\"small\" />\n </IconButton>\n </>\n )}>\n {message ?? t('install.banner')}\n </PromptAlert>\n </PromptRoot>\n <InstallInstructionsDialog variant={variant} open={stepsOpen} onClose={handleStepsClose} />\n </>\n </ComponentTexts>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst InstallPromptBanner = withPieceSlots(InstallPromptBannerContent, THEME_NAME);\nexport default InstallPromptBanner;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA+BA,MAAM,aAAa;AACnB,MAAM,aAAa,UAAU,YAAY,QAAQ,QAAQ;AACzD,MAAM,cAAc,UAAU,YAAY,SAAS,KAAK;AAGxD,MAAM,qBAAqB;AAW3B,SAAS,2BAA2B,OAAO;AAEvC,QAAM,EAAC,SAAS,iBAAiB,aAAa,WAAW,OAAO,GAAG,aAAA,IAAgB,cAAc,OAAO,UAAU;AAClH,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,SAAS,WAAW,eAAe,QAAA,IAAW,iBAAA;AACrD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAGhD,MAAI,CAAC,WAAW,WAAW;AACvB,WAAO;AAAA,EACX;AAEA,QAAM,WAAW,YAAY,gBAAgB;AAE7C,QAAM,eAAe,YAAY;AAC7B,QAAI,UAAU;AACV,wBAAkB,MAAM,eAAe;AAAA,IAC3C,OAAO;AACH,mBAAa,IAAI;AACjB,oBAAA;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,gBAAgB,MAAM;AACxB,YAAA;AACA,gBAAA;AAAA,EACJ;AAGA,QAAM,mBAAmB,MAAM;AAC3B,iBAAa,KAAK;AAClB,kBAAA;AAAA,EACJ;AAEA,SACI,oBAAC,gBAAA,EAAe,WAAU,UAAS,OAC/B,UAAA,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAW,MAAI;AAAA,QAAC,cAAc,EAAC,UAAU,UAAU,YAAY,SAAA;AAAA,QACtD,OAAO,CAAC,UAAW,iBAAiB,KAAK,IACnC,oBAAoB,kBAAkB,IACtC,EAAC,IAAI,8BAAA;AAAA,QACjB,UAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YAAY,UAAS;AAAA,YAAO,0BAAO,0BAAA,EAAyB;AAAA,YAAI,OAAO,EAAC,OAAO,QAAQ,YAAY,SAAA;AAAA,YAC7F,QACI,qBAAA,UAAA,EACI,UAAA;AAAA,cAAA,oBAAC,QAAA,EAAO,OAAM,WAAU,MAAK,SAAQ,SAAS,cACzC,UAAA,WAAW,EAAE,gBAAgB,IAAI,EAAE,eAAe,GACvD;AAAA,kCACC,YAAA,EAAW,MAAK,SAAQ,OAAM,WAAU,cAAY,EAAE,iBAAiB,GAAG,SAAS,eAChF,UAAA,oBAAC,kBAAA,EAAiB,UAAS,SAAQ,EAAA,CACvC;AAAA,YAAA,GACJ;AAAA,YAEN,UAAA,WAAW,EAAE,gBAAgB;AAAA,UAAA;AAAA,QAAA;AAAA,MAClC;AAAA,IAAA;AAAA,wBAEH,2BAAA,EAA0B,SAAkB,MAAM,WAAW,SAAS,iBAAA,CAAkB;AAAA,EAAA,EAAA,CAC7F,EAAA,CACJ;AAER;AAGA,MAAM,sBAAsB,eAAe,4BAA4B,UAAU;"}
|
package/dist/react-hook-form.js
CHANGED
|
@@ -14,6 +14,7 @@ import { default as default11 } from "./hookForm/FormProblemAlert.js";
|
|
|
14
14
|
import { default as default12 } from "./hookForm/FormRepeatingFields.js";
|
|
15
15
|
import { default as default13 } from "./hookForm/FormTransferListField.js";
|
|
16
16
|
import { listTotalAtMostRule, listTotalOf, orderedInListRule, uniqueInListRule } from "./forms/listRules.js";
|
|
17
|
+
import { percentRule, sameAsFieldRule } from "./forms/fieldRules.js";
|
|
17
18
|
import { default as default14 } from "./hookForm/FormSelectField.js";
|
|
18
19
|
import { default as default15 } from "./hookForm/FormWizard.js";
|
|
19
20
|
import { default as default16 } from "./hookForm/FormTextField.js";
|
|
@@ -44,6 +45,8 @@ export {
|
|
|
44
45
|
listTotalAtMostRule,
|
|
45
46
|
listTotalOf,
|
|
46
47
|
orderedInListRule,
|
|
48
|
+
percentRule,
|
|
49
|
+
sameAsFieldRule,
|
|
47
50
|
uniqueInListRule,
|
|
48
51
|
useFieldArray,
|
|
49
52
|
useForm,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-hook-form.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"react-hook-form.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -6,13 +6,13 @@ import FormHelperText from "@mui/material/FormHelperText";
|
|
|
6
6
|
import Paper from "@mui/material/Paper";
|
|
7
7
|
import Stack from "@mui/material/Stack";
|
|
8
8
|
import Box from "@mui/material/Box";
|
|
9
|
-
import {
|
|
9
|
+
import { useValidationText } from "../forms/useValidationText.js";
|
|
10
10
|
import FieldFrame from "../inputs/FieldFrame.js";
|
|
11
11
|
import { richTextContentSxOf } from "./richTextContentSx.js";
|
|
12
12
|
import { richTextExtensionsOf } from "./richTextExtensions.js";
|
|
13
13
|
import { DEFAULT_RICH_TEXT_FEATURES, normalizedHtmlOf } from "./richTextRules.js";
|
|
14
14
|
import RichTextToolbar from "./RichTextToolbar.js";
|
|
15
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
15
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
16
16
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
17
17
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
18
18
|
const THEME_NAME = "CalcitaRichTextEditor";
|
|
@@ -21,7 +21,7 @@ const EditorToolbar = piecePart(THEME_NAME, "Toolbar", Stack);
|
|
|
21
21
|
const EditorContentArea = piecePart(THEME_NAME, "Content", Box);
|
|
22
22
|
const EditorFooter = piecePart(THEME_NAME, "Footer", Stack);
|
|
23
23
|
const DEFAULT_MIN_HEIGHT = 160;
|
|
24
|
-
function
|
|
24
|
+
function RichTextEditorContent(props) {
|
|
25
25
|
const {
|
|
26
26
|
name,
|
|
27
27
|
label,
|
|
@@ -39,15 +39,17 @@ function RichTextEditor(props) {
|
|
|
39
39
|
minHeight = DEFAULT_MIN_HEIGHT,
|
|
40
40
|
disabled = false,
|
|
41
41
|
sx,
|
|
42
|
+
messages,
|
|
42
43
|
texts,
|
|
43
44
|
...unknownProps
|
|
44
45
|
} = usePieceProps(props, THEME_NAME);
|
|
45
46
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
46
47
|
const { t } = useToolkitTranslation("richText", texts);
|
|
48
|
+
const validationTextOf = useValidationText({ name, label, messages });
|
|
47
49
|
const generatedId = useId();
|
|
48
50
|
const contentId = `${name ?? "rich-text"}-${generatedId}`;
|
|
49
51
|
const labelId = `${contentId}-label`;
|
|
50
|
-
const errorText =
|
|
52
|
+
const errorText = validationTextOf(error);
|
|
51
53
|
const latest = useRef({ onChange, onBlur, onEditorReady });
|
|
52
54
|
latest.current = { onChange, onBlur, onEditorReady };
|
|
53
55
|
const editor = useEditor({
|
|
@@ -102,6 +104,7 @@ function RichTextEditor(props) {
|
|
|
102
104
|
] })
|
|
103
105
|
] }) });
|
|
104
106
|
}
|
|
107
|
+
const RichTextEditor = withPieceSlots(RichTextEditorContent, THEME_NAME);
|
|
105
108
|
export {
|
|
106
109
|
RichTextEditor as default
|
|
107
110
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichTextEditor.js","sources":["../../src/richText/RichTextEditor.jsx"],"sourcesContent":["// El editor de texto con formato (Tiptap), con la etiqueta arriba como los demás campos del paquete: negrita, cursiva,\n// títulos, listas, cita, código, ligas seguras, alineación, deshacer y rehacer, y el contador de caracteres si hay tope.\n//\n// Viaja HTML ('<p>Hola <strong>mundo</strong></p>'); '' si no hay nada escrito (así required() funciona). Para ENSEÑAR lo\n// guardado va RichTextView, que solo deja pasar lo que el editor entiende: un <script> o un onclick pegado no llegan a la\n// página.\n//\n// Extensible sin tocar el paquete: features (qué botones trae; lo que no se pide tampoco entra al documento), extensions\n// (extensiones de Tiptap del proyecto: menciones, tablas…), toolbarActions (botones propios al final de la barra) y\n// onEditorReady (el editor de Tiptap, para lo que la barra no trae). Con formik: FormRichTextField; con react-hook-form, dentro\n// de un Controller.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRichTextEditor con defaultProps y\n// styleOverrides de sus partes: root, toolbar, content, footer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useRef} from 'react';\nimport {EditorContent, useEditor, useEditorState} from '@tiptap/react';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Box from '@mui/material/Box';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {richTextContentSxOf} from '@/richText/richTextContentSx.js';\nimport {richTextExtensionsOf} from '@/richText/richTextExtensions.js';\nimport {DEFAULT_RICH_TEXT_FEATURES, normalizedHtmlOf} from '@/richText/richTextRules.js';\nimport RichTextToolbar from '@/richText/RichTextToolbar.jsx';\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.CalcitaRichTextEditor)\nconst THEME_NAME = 'CalcitaRichTextEditor';\nconst EditorRoot = piecePart(THEME_NAME, 'Root', Paper);\nconst EditorToolbar = piecePart(THEME_NAME, 'Toolbar', Stack);\nconst EditorContentArea = piecePart(THEME_NAME, 'Content', Box);\nconst EditorFooter = piecePart(THEME_NAME, 'Footer', Stack);\n\nconst DEFAULT_MIN_HEIGHT = 160;\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {string|null|undefined} props.value el HTML guardado ('' sin texto)\n * @param {(html: string) => void} props.onChange recibe el HTML ('' si se borra todo)\n * @param {() => void} [props.onBlur]\n * @param {string} [props.placeholder] ya traducido: lo que se ve con el editor vacío\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {number} [props.maxLength] el tope de caracteres (con su contador abajo)\n * @param {string[]} [props.features] los botones de la barra (RICH_TEXT_FEATURE); por omisión todos\n * @param {Array<object>} [props.extensions] extensiones de Tiptap del proyecto\n * @param {import('react').ReactNode} [props.toolbarActions] botones propios al final de la barra\n * @param {(editor: import('@tiptap/react').Editor) => void} [props.onEditorReady] el editor de Tiptap, al estar listo\n * @param {number} [props.minHeight] el alto mínimo del texto en px; por omisión 160\n * @param {boolean} [props.disabled]\n * @param {object} [props.sx]\n * @param {object} [props.texts] los textos que cambian, con la forma de `richText` en los textos del paquete\n */\nexport default function RichTextEditor(props) {\n const {\n name, label, value, onChange, onBlur, placeholder, error, helperText = ' ', maxLength, features = DEFAULT_RICH_TEXT_FEATURES, extensions,\n toolbarActions, onEditorReady, minHeight = DEFAULT_MIN_HEIGHT, disabled = false, sx, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('richText', texts);\n const generatedId = useId();\n const contentId = `${name ?? 'rich-text'}-${generatedId}`;\n const labelId = `${contentId}-label`;\n const errorText = translateValidationMessage(t, error);\n // lo último de afuera, sin rearmar el editor cada vez que la pantalla pinta otra función\n const latest = useRef({onChange, onBlur, onEditorReady});\n latest.current = {onChange, onBlur, onEditorReady};\n\n const editor = useEditor({\n extensions: richTextExtensionsOf({features, placeholder, maxLength, extensions}),\n content: value ?? '',\n editable: !disabled,\n // OJO: sin esto, al pintar en el servidor (Next) el editor avisa que no hay documento todavía\n immediatelyRender: false,\n editorProps: {\n attributes: {id: contentId, role: 'textbox', 'aria-multiline': 'true', 'aria-labelledby': labelId, ...(errorText && {'aria-invalid': 'true'})},\n },\n onUpdate: ({editor: current}) => latest.current.onChange(normalizedHtmlOf(current.getHTML())),\n onBlur: () => latest.current.onBlur?.(),\n onCreate: ({editor: current}) => latest.current.onEditorReady?.(current),\n }, [features.join(','), placeholder, maxLength, Boolean(errorText)]);\n\n // lo que llega de afuera (el formulario se reinició, llegó del servidor) entra sin avisar otro cambio\n useEffect(() => {\n if (editor && normalizedHtmlOf(editor.getHTML()) !== (value ?? '')) {\n editor.commands.setContent(value ?? '', {emitUpdate: false});\n }\n }, [editor, value]);\n useEffect(() => {\n editor?.setEditable(!disabled);\n }, [editor, disabled]);\n\n const characters = useEditorState({editor, selector: ({editor: current}) => current?.storage.characterCount?.characters() ?? 0});\n\n return (\n <FieldFrame inputId={contentId} labelId={labelId} label={label}>\n <div>\n <EditorRoot variant=\"outlined\" ownSx={(theme) => ({\n borderRadius: 2, overflow: 'hidden', opacity: disabled ? 0.6 : 1,\n borderColor: errorText ? 'error.main' : 'divider',\n '&:focus-within': {borderColor: errorText ? 'error.main' : 'primary.main', boxShadow: `0 0 0 1px ${(theme.vars ?? theme).palette[errorText ? 'error' : 'primary'].main}`},\n })} sx={sx}>\n <EditorToolbar role=\"toolbar\" aria-label={t('toolbar.label')} aria-controls={contentId} direction=\"row\"\n ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 0.25, px: 0.5, py: 0.5, borderBottom: 1, borderColor: 'divider'}}>\n <RichTextToolbar editor={editor} features={features} disabled={disabled} t={t} actions={toolbarActions} />\n </EditorToolbar>\n <EditorContentArea ownSx={(theme) => {\n const content = richTextContentSxOf(theme);\n return {...content, px: 2, py: 1.5, cursor: 'text', '& .tiptap': {...content['& .tiptap'], minHeight}};\n }} onClick={() => editor?.commands.focus()}>\n <EditorContent editor={editor} />\n </EditorContentArea>\n </EditorRoot>\n <EditorFooter direction=\"row\" ownSx={{justifyContent: 'space-between', gap: 2}}>\n <FormHelperText error={Boolean(errorText)}>{errorText ?? helperText}</FormHelperText>\n {maxLength && (\n <FormHelperText sx={{fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap'}}>\n {t('counter', {count: characters, max: maxLength})}\n </FormHelperText>\n )}\n </EditorFooter>\n </div>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RichTextEditor.js","sources":["../../src/richText/RichTextEditor.jsx"],"sourcesContent":["// El editor de texto con formato (Tiptap), con la etiqueta arriba como los demás campos del paquete: negrita, cursiva,\n// títulos, listas, cita, código, ligas seguras, alineación, deshacer y rehacer, y el contador de caracteres si hay tope.\n//\n// Viaja HTML ('<p>Hola <strong>mundo</strong></p>'); '' si no hay nada escrito (así required() funciona). Para ENSEÑAR lo\n// guardado va RichTextView, que solo deja pasar lo que el editor entiende: un <script> o un onclick pegado no llegan a la\n// página.\n//\n// Extensible sin tocar el paquete: features (qué botones trae; lo que no se pide tampoco entra al documento), extensions\n// (extensiones de Tiptap del proyecto: menciones, tablas…), toolbarActions (botones propios al final de la barra) y\n// onEditorReady (el editor de Tiptap, para lo que la barra no trae). Con formik: FormRichTextField; con react-hook-form, dentro\n// de un Controller.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRichTextEditor con defaultProps y\n// styleOverrides de sus partes: root, toolbar, content, footer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useRef} from 'react';\nimport {EditorContent, useEditor, useEditorState} from '@tiptap/react';\nimport FormHelperText from '@mui/material/FormHelperText';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Box from '@mui/material/Box';\n\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {richTextContentSxOf} from '@/richText/richTextContentSx.js';\nimport {richTextExtensionsOf} from '@/richText/richTextExtensions.js';\nimport {DEFAULT_RICH_TEXT_FEATURES, normalizedHtmlOf} from '@/richText/richTextRules.js';\nimport RichTextToolbar from '@/richText/RichTextToolbar.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaRichTextEditor)\nconst THEME_NAME = 'CalcitaRichTextEditor';\nconst EditorRoot = piecePart(THEME_NAME, 'Root', Paper);\nconst EditorToolbar = piecePart(THEME_NAME, 'Toolbar', Stack);\nconst EditorContentArea = piecePart(THEME_NAME, 'Content', Box);\nconst EditorFooter = piecePart(THEME_NAME, 'Footer', Stack);\n\nconst DEFAULT_MIN_HEIGHT = 160;\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {string|null|undefined} props.value el HTML guardado ('' sin texto)\n * @param {(html: string) => void} props.onChange recibe el HTML ('' si se borra todo)\n * @param {() => void} [props.onBlur]\n * @param {string} [props.placeholder] ya traducido: lo que se ve con el editor vacío\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida\n * @param {number} [props.maxLength] el tope de caracteres (con su contador abajo)\n * @param {string[]} [props.features] los botones de la barra (RICH_TEXT_FEATURE); por omisión todos\n * @param {Array<object>} [props.extensions] extensiones de Tiptap del proyecto\n * @param {import('react').ReactNode} [props.toolbarActions] botones propios al final de la barra\n * @param {(editor: import('@tiptap/react').Editor) => void} [props.onEditorReady] el editor de Tiptap, al estar listo\n * @param {number} [props.minHeight] el alto mínimo del texto en px; por omisión 160\n * @param {boolean} [props.disabled]\n * @param {object} [props.sx]\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Escriba el RFC', matches: 'El RFC lleva 12 o 13 caracteres'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `richText` en los textos del paquete\n */\nfunction RichTextEditorContent(props) {\n const {\n name, label, value, onChange, onBlur, placeholder, error, helperText = ' ', maxLength, features = DEFAULT_RICH_TEXT_FEATURES, extensions,\n toolbarActions, onEditorReady, minHeight = DEFAULT_MIN_HEIGHT, disabled = false, sx, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('richText', texts);\n // el texto del error: el de la pantalla (messages), el del proyecto para este campo o el de Calcita\n const validationTextOf = useValidationText({name, label, messages});\n const generatedId = useId();\n const contentId = `${name ?? 'rich-text'}-${generatedId}`;\n const labelId = `${contentId}-label`;\n const errorText = validationTextOf(error);\n // lo último de afuera, sin rearmar el editor cada vez que la pantalla pinta otra función\n const latest = useRef({onChange, onBlur, onEditorReady});\n latest.current = {onChange, onBlur, onEditorReady};\n\n const editor = useEditor({\n extensions: richTextExtensionsOf({features, placeholder, maxLength, extensions}),\n content: value ?? '',\n editable: !disabled,\n // OJO: sin esto, al pintar en el servidor (Next) el editor avisa que no hay documento todavía\n immediatelyRender: false,\n editorProps: {\n attributes: {id: contentId, role: 'textbox', 'aria-multiline': 'true', 'aria-labelledby': labelId, ...(errorText && {'aria-invalid': 'true'})},\n },\n onUpdate: ({editor: current}) => latest.current.onChange(normalizedHtmlOf(current.getHTML())),\n onBlur: () => latest.current.onBlur?.(),\n onCreate: ({editor: current}) => latest.current.onEditorReady?.(current),\n }, [features.join(','), placeholder, maxLength, Boolean(errorText)]);\n\n // lo que llega de afuera (el formulario se reinició, llegó del servidor) entra sin avisar otro cambio\n useEffect(() => {\n if (editor && normalizedHtmlOf(editor.getHTML()) !== (value ?? '')) {\n editor.commands.setContent(value ?? '', {emitUpdate: false});\n }\n }, [editor, value]);\n useEffect(() => {\n editor?.setEditable(!disabled);\n }, [editor, disabled]);\n\n const characters = useEditorState({editor, selector: ({editor: current}) => current?.storage.characterCount?.characters() ?? 0});\n\n return (\n <FieldFrame inputId={contentId} labelId={labelId} label={label}>\n <div>\n <EditorRoot variant=\"outlined\" ownSx={(theme) => ({\n borderRadius: 2, overflow: 'hidden', opacity: disabled ? 0.6 : 1,\n borderColor: errorText ? 'error.main' : 'divider',\n '&:focus-within': {borderColor: errorText ? 'error.main' : 'primary.main', boxShadow: `0 0 0 1px ${(theme.vars ?? theme).palette[errorText ? 'error' : 'primary'].main}`},\n })} sx={sx}>\n <EditorToolbar role=\"toolbar\" aria-label={t('toolbar.label')} aria-controls={contentId} direction=\"row\"\n ownSx={{alignItems: 'center', flexWrap: 'wrap', gap: 0.25, px: 0.5, py: 0.5, borderBottom: 1, borderColor: 'divider'}}>\n <RichTextToolbar editor={editor} features={features} disabled={disabled} t={t} actions={toolbarActions} />\n </EditorToolbar>\n <EditorContentArea ownSx={(theme) => {\n const content = richTextContentSxOf(theme);\n return {...content, px: 2, py: 1.5, cursor: 'text', '& .tiptap': {...content['& .tiptap'], minHeight}};\n }} onClick={() => editor?.commands.focus()}>\n <EditorContent editor={editor} />\n </EditorContentArea>\n </EditorRoot>\n <EditorFooter direction=\"row\" ownSx={{justifyContent: 'space-between', gap: 2}}>\n <FormHelperText error={Boolean(errorText)}>{errorText ?? helperText}</FormHelperText>\n {maxLength && (\n <FormHelperText sx={{fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap'}}>\n {t('counter', {count: characters, max: maxLength})}\n </FormHelperText>\n )}\n </EditorFooter>\n </div>\n </FieldFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst RichTextEditor = withPieceSlots(RichTextEditorContent, THEME_NAME);\nexport default RichTextEditor;\n"],"names":[],"mappings":"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"}
|
|
@@ -5,11 +5,11 @@ import Box from "@mui/material/Box";
|
|
|
5
5
|
import { richTextContentSxOf } from "./richTextContentSx.js";
|
|
6
6
|
import { richTextExtensionsOf } from "./richTextExtensions.js";
|
|
7
7
|
import { DEFAULT_RICH_TEXT_FEATURES } from "./richTextRules.js";
|
|
8
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
8
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
9
9
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
10
10
|
const THEME_NAME = "CalcitaRichTextView";
|
|
11
11
|
const ViewRoot = piecePart(THEME_NAME, "Root", Box);
|
|
12
|
-
function
|
|
12
|
+
function RichTextViewContent(props) {
|
|
13
13
|
const { value, features = DEFAULT_RICH_TEXT_FEATURES, extensions, sx, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
14
14
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
15
15
|
const editor = useEditor({
|
|
@@ -20,6 +20,7 @@ function RichTextView(props) {
|
|
|
20
20
|
}, [value, features.join(",")]);
|
|
21
21
|
return /* @__PURE__ */ jsx(ViewRoot, { ownSx: richTextContentSxOf, sx, children: /* @__PURE__ */ jsx(EditorContent, { editor }) });
|
|
22
22
|
}
|
|
23
|
+
const RichTextView = withPieceSlots(RichTextViewContent, THEME_NAME);
|
|
23
24
|
export {
|
|
24
25
|
RichTextView as default
|
|
25
26
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichTextView.js","sources":["../../src/richText/RichTextView.jsx"],"sourcesContent":["// Enseña el texto con formato que guardó RichTextEditor, sin poder editarlo. Pasa por el mismo esquema del editor: solo entra\n// lo que el editor entiende (párrafos, títulos, listas, ligas seguras…) y lo demás se queda afuera. Un <script>, un\n// onclick o una liga javascript: pegados en el HTML no llegan a la página (XSS), sin otra librería para limpiarlo.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRichTextView con defaultProps y\n// styleOverrides de su parte root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {EditorContent, useEditor} from '@tiptap/react';\nimport Box from '@mui/material/Box';\n\nimport {richTextContentSxOf} from '@/richText/richTextContentSx.js';\nimport {richTextExtensionsOf} from '@/richText/richTextExtensions.js';\nimport {DEFAULT_RICH_TEXT_FEATURES} from '@/richText/richTextRules.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 su parte (theme.components.CalcitaRichTextView)\nconst THEME_NAME = 'CalcitaRichTextView';\nconst ViewRoot = piecePart(THEME_NAME, 'Root', Box);\n\n/**\n * @param {object} props\n * @param {string|null|undefined} props.value el HTML guardado\n * @param {string[]} [props.features] lo que se deja pasar (por omisión, lo de un editor completo)\n * @param {Array<object>} [props.extensions] las extensiones de Tiptap del proyecto (las mismas que tiene su editor)\n * @param {object} [props.sx]\n */\
|
|
1
|
+
{"version":3,"file":"RichTextView.js","sources":["../../src/richText/RichTextView.jsx"],"sourcesContent":["// Enseña el texto con formato que guardó RichTextEditor, sin poder editarlo. Pasa por el mismo esquema del editor: solo entra\n// lo que el editor entiende (párrafos, títulos, listas, ligas seguras…) y lo demás se queda afuera. Un <script>, un\n// onclick o una liga javascript: pegados en el HTML no llegan a la página (XSS), sin otra librería para limpiarlo.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRichTextView con defaultProps y\n// styleOverrides de su parte root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {EditorContent, useEditor} from '@tiptap/react';\nimport Box from '@mui/material/Box';\n\nimport {richTextContentSxOf} from '@/richText/richTextContentSx.js';\nimport {richTextExtensionsOf} from '@/richText/richTextExtensions.js';\nimport {DEFAULT_RICH_TEXT_FEATURES} from '@/richText/richTextRules.js';\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 su parte (theme.components.CalcitaRichTextView)\nconst THEME_NAME = 'CalcitaRichTextView';\nconst ViewRoot = piecePart(THEME_NAME, 'Root', Box);\n\n/**\n * @param {object} props\n * @param {string|null|undefined} props.value el HTML guardado\n * @param {string[]} [props.features] lo que se deja pasar (por omisión, lo de un editor completo)\n * @param {Array<object>} [props.extensions] las extensiones de Tiptap del proyecto (las mismas que tiene su editor)\n * @param {object} [props.sx]\n */\nfunction RichTextViewContent(props) {\n const {value, features = DEFAULT_RICH_TEXT_FEATURES, extensions, sx, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const editor = useEditor({\n extensions: richTextExtensionsOf({features, extensions}),\n content: value ?? '',\n editable: false,\n immediatelyRender: false,\n }, [value, features.join(',')]);\n\n return (\n <ViewRoot ownSx={richTextContentSxOf} sx={sx}>\n <EditorContent editor={editor} />\n </ViewRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst RichTextView = withPieceSlots(RichTextViewContent, THEME_NAME);\nexport default RichTextView;\n"],"names":[],"mappings":";;;;;;;;;AAkBA,MAAM,aAAa;AACnB,MAAM,WAAW,UAAU,YAAY,QAAQ,GAAG;AASlD,SAAS,oBAAoB,OAAO;AAChC,QAAM,EAAC,OAAO,WAAW,4BAA4B,YAAY,IAAI,GAAG,aAAA,IAAgB,cAAc,OAAO,UAAU;AACvH,mBAAiB,YAAY,YAAY;AACzC,QAAM,SAAS,UAAU;AAAA,IACrB,YAAY,qBAAqB,EAAC,UAAU,YAAW;AAAA,IACvD,SAAS,SAAS;AAAA,IAClB,UAAU;AAAA,IACV,mBAAmB;AAAA,EAAA,GACpB,CAAC,OAAO,SAAS,KAAK,GAAG,CAAC,CAAC;AAE9B,SACI,oBAAC,YAAS,OAAO,qBAAqB,IAClC,UAAA,oBAAC,eAAA,EAAc,QAAgB,EAAA,CACnC;AAER;AAGA,MAAM,eAAe,eAAe,qBAAqB,UAAU;"}
|
|
@@ -21,7 +21,7 @@ import SyncLockOutlinedIcon from "@mui/icons-material/SyncLockOutlined";
|
|
|
21
21
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
22
22
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
23
23
|
import SectionCard from "../ui/layout/SectionCard.js";
|
|
24
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
24
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
25
25
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
26
26
|
const THEME_NAME = "CalcitaActivityLog";
|
|
27
27
|
const ActivityItemIcon = piecePart(THEME_NAME, "ItemIcon", ListItemIcon);
|
|
@@ -53,7 +53,7 @@ const EVENT_LOOK = Object.freeze({
|
|
|
53
53
|
});
|
|
54
54
|
const DEFAULT_LOOK = Object.freeze({ Icon: EventNoteOutlinedIcon });
|
|
55
55
|
const LOADING_HEIGHT_PIXELS = 160;
|
|
56
|
-
function
|
|
56
|
+
function ActivityLogContent(props) {
|
|
57
57
|
const { events, error = null, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
58
58
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
59
59
|
const { t } = useToolkitTranslation("security", texts);
|
|
@@ -79,6 +79,7 @@ function ActivityLog(props) {
|
|
|
79
79
|
}) })
|
|
80
80
|
] }) });
|
|
81
81
|
}
|
|
82
|
+
const ActivityLog = withPieceSlots(ActivityLogContent, THEME_NAME);
|
|
82
83
|
export {
|
|
83
84
|
ACCOUNT_EVENT_KIND,
|
|
84
85
|
ActivityLog as default
|