@stackcraftmx/calcita 0.32.2 → 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 +426 -4
- package/cli/calcita.js +15 -0
- package/cli/checkTexts.js +209 -0
- package/cli/packageVersions.js +17 -3
- package/cli/projectFiles.js +7 -2
- package/cli/templates/module/__Name__Page.test.jsx.tpl +24 -0
- package/cli/templates/project/README.md.tpl +35 -2
- package/cli/templates/project/eslint.config.js.tpl +27 -0
- package/cli/templates/project/src/app/appRouter.jsx.tpl +5 -2
- package/cli/templates/project/src/app/appTexts.js.tpl +2 -1
- package/cli/templates/project/src/core/accountConnector.js.tpl +4 -2
- package/cli/templates/project/src/core/publicPaths.js.tpl +10 -2
- package/cli/templates/project/src/modules/auth/SignInPage.test.jsx.tpl +20 -0
- package/cli/templates/project/src/modules/home/HomePage.test.jsx.tpl +16 -0
- package/cli/templates/project/src/testing/browserGaps.js.tpl +70 -0
- package/cli/templates/project/src/testing/openScreen.jsx.tpl +58 -0
- package/cli/templates/project/src/testing/setup.js.tpl +25 -0
- package/cli/templates/project/vite.config.js.tpl +20 -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 +71 -14
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +97 -28
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/ServerTableFilterDrawer.js +57 -0
- package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -0
- 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/filterContext.js.map +1 -1
- package/dist/DataTable/fixedColumns.js +24 -0
- package/dist/DataTable/fixedColumns.js.map +1 -0
- package/dist/DataTable/goToPageOptions.js +17 -0
- package/dist/DataTable/goToPageOptions.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/date-pickers.js +28 -2
- package/dist/date-pickers.js.map +1 -1
- package/dist/datePickers/DateInput.js +41 -54
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +81 -0
- package/dist/datePickers/DateTimeInput.js.map +1 -0
- package/dist/datePickers/FormDateTimeField.js +12 -0
- package/dist/datePickers/FormDateTimeField.js.map +1 -0
- package/dist/datePickers/FormMonthField.js +12 -0
- package/dist/datePickers/FormMonthField.js.map +1 -0
- package/dist/datePickers/FormTimeField.js +12 -0
- package/dist/datePickers/FormTimeField.js.map +1 -0
- package/dist/datePickers/MonthInput.js +64 -0
- package/dist/datePickers/MonthInput.js.map +1 -0
- package/dist/datePickers/PickerFrame.js +27 -0
- package/dist/datePickers/PickerFrame.js.map +1 -0
- package/dist/datePickers/TimeInput.js +61 -0
- package/dist/datePickers/TimeInput.js.map +1 -0
- package/dist/datePickers/dateTimeRules.js +24 -0
- package/dist/datePickers/dateTimeRules.js.map +1 -0
- package/dist/datePickers/pickerValues.js +106 -0
- package/dist/datePickers/pickerValues.js.map +1 -0
- package/dist/datePickers/useCurrentMinute.js +28 -0
- package/dist/datePickers/useCurrentMinute.js.map +1 -0
- package/dist/datePickers/usePickerField.js +23 -0
- package/dist/datePickers/usePickerField.js.map +1 -0
- package/dist/dictionaries/dictionaries.js +3 -1
- package/dist/dictionaries/dictionaries.js.map +1 -1
- package/dist/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/FormAttachmentsField.js +12 -0
- package/dist/forms/FormAttachmentsField.js.map +1 -0
- package/dist/forms/FormCheckboxGroupField.js +12 -0
- package/dist/forms/FormCheckboxGroupField.js.map +1 -0
- package/dist/forms/FormFileField.js +12 -0
- package/dist/forms/FormFileField.js.map +1 -0
- package/dist/forms/FormFileUploadField.js +12 -0
- package/dist/forms/FormFileUploadField.js.map +1 -0
- package/dist/forms/FormImageField.js +12 -0
- package/dist/forms/FormImageField.js.map +1 -0
- package/dist/forms/FormMaskedField.js +12 -0
- package/dist/forms/FormMaskedField.js.map +1 -0
- package/dist/forms/FormProblemAlert.js +3 -2
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/FormRepeatingFields.js +54 -0
- package/dist/forms/FormRepeatingFields.js.map +1 -0
- package/dist/forms/FormTransferListField.js +12 -0
- package/dist/forms/FormTransferListField.js.map +1 -0
- package/dist/forms/SubmitButton.js +7 -2
- package/dist/forms/SubmitButton.js.map +1 -1
- package/dist/forms/applyServerProblem.js +10 -3
- package/dist/forms/applyServerProblem.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/listRules.js +111 -0
- package/dist/forms/listRules.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/serverProblemFields.js +17 -0
- package/dist/forms/serverProblemFields.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 +54 -29
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormAttachmentsField.js +12 -0
- package/dist/hookForm/FormAttachmentsField.js.map +1 -0
- package/dist/hookForm/FormCheckboxGroupField.js +12 -0
- package/dist/hookForm/FormCheckboxGroupField.js.map +1 -0
- package/dist/hookForm/FormFileField.js +12 -0
- package/dist/hookForm/FormFileField.js.map +1 -0
- package/dist/hookForm/FormFileUploadField.js +12 -0
- package/dist/hookForm/FormFileUploadField.js.map +1 -0
- package/dist/hookForm/FormImageField.js +12 -0
- package/dist/hookForm/FormImageField.js.map +1 -0
- package/dist/hookForm/FormMaskedField.js +12 -0
- package/dist/hookForm/FormMaskedField.js.map +1 -0
- package/dist/hookForm/FormProblemAlert.js +3 -2
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/hookForm/FormRepeatingFields.js +44 -0
- package/dist/hookForm/FormRepeatingFields.js.map +1 -0
- package/dist/hookForm/FormTransferListField.js +12 -0
- package/dist/hookForm/FormTransferListField.js.map +1 -0
- package/dist/hookForm/SubmitButton.js +7 -2
- package/dist/hookForm/SubmitButton.js.map +1 -1
- package/dist/hookForm/applyServerProblem.js +8 -3
- package/dist/hookForm/applyServerProblem.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 +235 -124
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +103 -29
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +103 -0
- package/dist/inputs/CheckboxGroupInput.js.map +1 -0
- 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 +87 -0
- package/dist/inputs/MaskedInput.js.map +1 -0
- package/dist/inputs/NumberInput.js +41 -25
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/RepeatingFields.js +180 -0
- package/dist/inputs/RepeatingFields.js.map +1 -0
- package/dist/inputs/SelectInput.js +50 -9
- 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 +218 -0
- package/dist/inputs/TransferListInput.js.map +1 -0
- package/dist/inputs/WizardLayout.js +3 -2
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/inputs/inputMasks.js +84 -0
- package/dist/inputs/inputMasks.js.map +1 -0
- package/dist/inputs/selectAllState.js +20 -0
- package/dist/inputs/selectAllState.js.map +1 -0
- package/dist/inputs/useRowKeys.js +26 -0
- package/dist/inputs/useRowKeys.js.map +1 -0
- package/dist/kanban/KanbanBoard.js +3 -2
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/legal/CookieNotice.js +80 -0
- package/dist/legal/CookieNotice.js.map +1 -0
- package/dist/legal/TermsAcceptanceDialog.js +130 -0
- package/dist/legal/TermsAcceptanceDialog.js.map +1 -0
- package/dist/legal/consentStorage.js +18 -0
- package/dist/legal/consentStorage.js.map +1 -0
- package/dist/legal/useCookieConsent.js +17 -0
- package/dist/legal/useCookieConsent.js.map +1 -0
- package/dist/locales/en.js +257 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +257 -1
- 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 +82 -0
- package/dist/mexico/CfdiSummary.js.map +1 -0
- package/dist/mexico/EfirmaFields.js +168 -0
- package/dist/mexico/EfirmaFields.js.map +1 -0
- package/dist/mexico/certificateReader.js +79 -0
- package/dist/mexico/certificateReader.js.map +1 -0
- package/dist/mexico/cfdiReader.js +81 -0
- package/dist/mexico/cfdiReader.js.map +1 -0
- package/dist/mexico/derReader.js +81 -0
- package/dist/mexico/derReader.js.map +1 -0
- package/dist/mexico/efirmaKey.js +51 -0
- package/dist/mexico/efirmaKey.js.map +1 -0
- package/dist/mexico/mexicoIdentifiers.js +91 -0
- package/dist/mexico/mexicoIdentifiers.js.map +1 -0
- package/dist/mexico/mexicoRules.js +21 -0
- package/dist/mexico/mexicoRules.js.map +1 -0
- package/dist/mexico.js +34 -0
- package/dist/mexico.js.map +1 -0
- 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 +43 -17
- package/dist/react-hook-form.js.map +1 -1
- package/dist/rich-text.js +19 -0
- package/dist/rich-text.js.map +1 -0
- package/dist/richText/FormRichTextField.js +12 -0
- package/dist/richText/FormRichTextField.js.map +1 -0
- package/dist/richText/RichTextEditor.js +111 -0
- package/dist/richText/RichTextEditor.js.map +1 -0
- package/dist/richText/RichTextLinkPopover.js +57 -0
- package/dist/richText/RichTextLinkPopover.js.map +1 -0
- package/dist/richText/RichTextToolbar.js +189 -0
- package/dist/richText/RichTextToolbar.js.map +1 -0
- package/dist/richText/RichTextView.js +27 -0
- package/dist/richText/RichTextView.js.map +1 -0
- package/dist/richText/richTextContentSx.js +42 -0
- package/dist/richText/richTextContentSx.js.map +1 -0
- package/dist/richText/richTextExtensions.js +44 -0
- package/dist/richText/richTextExtensions.js.map +1 -0
- package/dist/richText/richTextRules.js +46 -0
- package/dist/richText/richTextRules.js.map +1 -0
- package/dist/security/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 +23 -2
- package/dist/table.js.map +1 -1
- package/dist/theme/components/glassComponents.js +27 -0
- package/dist/theme/components/glassComponents.js.map +1 -1
- package/dist/theme/components/minimalComponents.js +12 -0
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/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 +14 -0
- 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/filterContext.d.ts +2 -1
- package/dist/types/DataTable/fixedColumns.d.ts +29 -0
- package/dist/types/DataTable/goToPageOptions.d.ts +15 -0
- package/dist/types/DataTable/index.d.ts +7 -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/date-pickers.d.ts +10 -1
- package/dist/types/datePickers/DateInput.d.ts +10 -23
- package/dist/types/datePickers/DateTimeInput.d.ts +30 -0
- package/dist/types/datePickers/FormDateTimeField.d.ts +7 -0
- package/dist/types/datePickers/FormMonthField.d.ts +7 -0
- package/dist/types/datePickers/FormTimeField.d.ts +7 -0
- package/dist/types/datePickers/MonthInput.d.ts +25 -0
- package/dist/types/datePickers/PickerFrame.d.ts +22 -0
- package/dist/types/datePickers/TimeInput.d.ts +24 -0
- package/dist/types/datePickers/dateTimeRules.d.ts +31 -0
- package/dist/types/datePickers/pickerValues.d.ts +114 -0
- package/dist/types/datePickers/useCurrentMinute.d.ts +5 -0
- package/dist/types/datePickers/usePickerField.d.ts +28 -0
- package/dist/types/dictionaries/dictionaries.d.ts +6 -3
- 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/FormAttachmentsField.d.ts +7 -0
- package/dist/types/forms/FormCheckboxGroupField.d.ts +7 -0
- package/dist/types/forms/FormFileField.d.ts +7 -0
- package/dist/types/forms/FormFileUploadField.d.ts +7 -0
- package/dist/types/forms/FormImageField.d.ts +7 -0
- package/dist/types/forms/FormMaskedField.d.ts +7 -0
- package/dist/types/forms/FormProblemAlert.d.ts +6 -5
- package/dist/types/forms/FormRepeatingFields.d.ts +25 -0
- package/dist/types/forms/FormTransferListField.d.ts +7 -0
- 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 +11 -1
- package/dist/types/forms/listRules.d.ts +48 -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/serverProblemFields.d.ts +13 -0
- 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/FormAttachmentsField.d.ts +8 -0
- package/dist/types/hookForm/FormCheckboxGroupField.d.ts +8 -0
- package/dist/types/hookForm/FormFileField.d.ts +8 -0
- package/dist/types/hookForm/FormFileUploadField.d.ts +8 -0
- package/dist/types/hookForm/FormImageField.d.ts +8 -0
- package/dist/types/hookForm/FormMaskedField.d.ts +9 -0
- package/dist/types/hookForm/FormProblemAlert.d.ts +6 -5
- package/dist/types/hookForm/FormRepeatingFields.d.ts +27 -0
- package/dist/types/hookForm/FormTransferListField.d.ts +8 -0
- 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 +33 -4
- package/dist/types/inputs/CatalogInput.d.ts +10 -21
- package/dist/types/inputs/CheckboxGroupInput.d.ts +28 -0
- 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 +38 -0
- package/dist/types/inputs/NumberInput.d.ts +7 -20
- package/dist/types/inputs/RepeatingFields.d.ts +31 -0
- package/dist/types/inputs/SelectInput.d.ts +10 -18
- package/dist/types/inputs/TextInput.d.ts +3 -0
- package/dist/types/inputs/TransferListInput.d.ts +30 -0
- package/dist/types/inputs/WizardLayout.d.ts +6 -19
- package/dist/types/inputs/inputMasks.d.ts +52 -0
- package/dist/types/inputs/selectAllState.d.ts +11 -0
- package/dist/types/inputs/useRowKeys.d.ts +11 -0
- package/dist/types/kanban/KanbanBoard.d.ts +6 -25
- package/dist/types/legal/CookieNotice.d.ts +20 -0
- package/dist/types/legal/TermsAcceptanceDialog.d.ts +20 -0
- package/dist/types/legal/consentStorage.d.ts +16 -0
- package/dist/types/legal/useCookieConsent.d.ts +13 -0
- package/dist/types/locales/en.d.ts +256 -0
- package/dist/types/locales/es.d.ts +256 -0
- package/dist/types/maps/MapView.d.ts +6 -17
- package/dist/types/mexico/CfdiSummary.d.ts +12 -0
- package/dist/types/mexico/EfirmaFields.d.ts +28 -0
- package/dist/types/mexico/certificateReader.d.ts +37 -0
- package/dist/types/mexico/cfdiReader.d.ts +95 -0
- package/dist/types/mexico/derReader.d.ts +54 -0
- package/dist/types/mexico/efirmaKey.d.ts +26 -0
- package/dist/types/mexico/mexicoIdentifiers.d.ts +68 -0
- package/dist/types/mexico/mexicoRules.d.ts +45 -0
- package/dist/types/mexico.d.ts +11 -0
- 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 +11 -1
- package/dist/types/rich-text.d.ts +5 -0
- package/dist/types/richText/FormRichTextField.d.ts +7 -0
- package/dist/types/richText/RichTextEditor.d.ts +27 -0
- package/dist/types/richText/RichTextLinkPopover.d.ts +17 -0
- package/dist/types/richText/RichTextToolbar.d.ts +15 -0
- package/dist/types/richText/RichTextView.d.ts +10 -0
- package/dist/types/richText/richTextContentSx.d.ts +7 -0
- package/dist/types/richText/richTextExtensions.d.ts +15 -0
- package/dist/types/richText/richTextRules.d.ts +41 -0
- package/dist/types/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 +24 -11
- package/dist/types/ui/actions/CopyButton.d.ts +8 -11
- 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/actions/tableRowText.d.ts +6 -0
- package/dist/types/ui/cards/RecordCard.d.ts +40 -0
- package/dist/types/ui/cards/RecordCardSkeleton.d.ts +12 -0
- package/dist/types/ui/countdown/Countdown.d.ts +22 -0
- package/dist/types/ui/countdown/countdownParts.d.ts +39 -0
- package/dist/types/ui/countdown/useCountdown.d.ts +14 -0
- package/dist/types/ui/documents/PdfViewerDialog.d.ts +14 -0
- package/dist/types/ui/filters/DateRangeFilter.d.ts +6 -10
- package/dist/types/ui/filters/FilterDrawer.d.ts +23 -0
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +6 -13
- package/dist/types/ui/filters/QuickFilters.d.ts +6 -12
- package/dist/types/ui/filters/filterDrawerFields.d.ts +139 -0
- package/dist/types/ui/help/HelpButton.d.ts +11 -0
- package/dist/types/ui/help/HelpCarouselDialog.d.ts +27 -0
- package/dist/types/ui/help/useHelpCarousel.d.ts +21 -0
- package/dist/types/ui/index.d.ts +10 -0
- package/dist/types/ui/labels/IconBadge.d.ts +34 -0
- package/dist/types/ui/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 +39 -0
- package/dist/types/ui/navigation/routeTabMatching.d.ts +27 -0
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +6 -23
- package/dist/types/upload/AttachmentsInput.d.ts +72 -0
- package/dist/types/upload/FileChip.d.ts +18 -0
- package/dist/types/upload/FileDropzone.d.ts +7 -19
- package/dist/types/upload/FileInput.d.ts +31 -0
- package/dist/types/upload/FileUploadInput.d.ts +56 -0
- package/dist/types/upload/ImageInput.d.ts +35 -0
- package/dist/types/upload/TemplateDownloadButton.d.ts +27 -0
- package/dist/types/upload/fileAppearance.d.ts +8 -0
- package/dist/types/upload/fileRules.d.ts +38 -0
- package/dist/types/upload/transferProgress.d.ts +18 -0
- package/dist/types/upload/uploadMachine.d.ts +6 -2
- package/dist/types/upload/uploadWithProgress.d.ts +6 -2
- package/dist/types/upload/useFilePicker.d.ts +39 -0
- package/dist/ui/actions/ActionsMenu.js +109 -17
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/CopyButton.js +37 -12
- 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/actions/tableRowText.js +7 -0
- package/dist/ui/actions/tableRowText.js.map +1 -0
- package/dist/ui/cards/RecordCard.js +146 -0
- package/dist/ui/cards/RecordCard.js.map +1 -0
- package/dist/ui/cards/RecordCardSkeleton.js +38 -0
- package/dist/ui/cards/RecordCardSkeleton.js.map +1 -0
- package/dist/ui/countdown/Countdown.js +39 -0
- package/dist/ui/countdown/Countdown.js.map +1 -0
- package/dist/ui/countdown/countdownParts.js +34 -0
- package/dist/ui/countdown/countdownParts.js.map +1 -0
- package/dist/ui/countdown/useCountdown.js +35 -0
- package/dist/ui/countdown/useCountdown.js.map +1 -0
- package/dist/ui/documents/PdfViewerDialog.js +82 -0
- package/dist/ui/documents/PdfViewerDialog.js.map +1 -0
- package/dist/ui/filters/DateRangeFilter.js +3 -2
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/FilterDrawer.js +212 -0
- package/dist/ui/filters/FilterDrawer.js.map +1 -0
- 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/filters/filterDrawerFields.js +119 -0
- package/dist/ui/filters/filterDrawerFields.js.map +1 -0
- package/dist/ui/help/HelpButton.js +22 -0
- package/dist/ui/help/HelpButton.js.map +1 -0
- package/dist/ui/help/HelpCarouselDialog.js +155 -0
- package/dist/ui/help/HelpCarouselDialog.js.map +1 -0
- package/dist/ui/help/useHelpCarousel.js +25 -0
- package/dist/ui/help/useHelpCarousel.js.map +1 -0
- package/dist/ui/labels/IconBadge.js +82 -0
- package/dist/ui/labels/IconBadge.js.map +1 -0
- package/dist/ui/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 +79 -0
- package/dist/ui/navigation/RouteTabs.js.map +1 -0
- package/dist/ui/navigation/routeTabMatching.js +23 -0
- package/dist/ui/navigation/routeTabMatching.js.map +1 -0
- package/dist/ui/timeline/GroupedTimeline.js +3 -2
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/AttachmentsInput.js +130 -0
- package/dist/upload/AttachmentsInput.js.map +1 -0
- package/dist/upload/FileChip.js +28 -0
- package/dist/upload/FileChip.js.map +1 -0
- package/dist/upload/FileDropzone.js +23 -87
- package/dist/upload/FileDropzone.js.map +1 -1
- package/dist/upload/FileInput.js +115 -0
- package/dist/upload/FileInput.js.map +1 -0
- package/dist/upload/FileUploadInput.js +288 -0
- package/dist/upload/FileUploadInput.js.map +1 -0
- package/dist/upload/ImageInput.js +128 -0
- package/dist/upload/ImageInput.js.map +1 -0
- package/dist/upload/TemplateDownloadButton.js +68 -0
- package/dist/upload/TemplateDownloadButton.js.map +1 -0
- package/dist/upload/fileAppearance.js +21 -0
- package/dist/upload/fileAppearance.js.map +1 -0
- package/dist/upload/fileRules.js +27 -1
- package/dist/upload/fileRules.js.map +1 -1
- package/dist/upload/transferProgress.js +23 -0
- package/dist/upload/transferProgress.js.map +1 -0
- package/dist/upload/uploadMachine.js +8 -8
- package/dist/upload/uploadMachine.js.map +1 -1
- package/dist/upload/uploadWithProgress.js +1 -1
- package/dist/upload/uploadWithProgress.js.map +1 -1
- package/dist/upload/useFilePicker.js +101 -0
- package/dist/upload/useFilePicker.js.map +1 -0
- package/package.json +44 -1
- package/cli/templates/project/src/app/combineTexts.js.tpl +0 -31
|
@@ -1,16 +1,82 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { createContext, useContext, createElement } from "react";
|
|
2
3
|
import { styled, useThemeProps } from "@mui/material/styles";
|
|
3
4
|
const PIECE_ONLY_PROPS = /* @__PURE__ */ new Set(["ownSx", "ownerState", "theme", "as"]);
|
|
5
|
+
const SLOT_PROP_NAMES = Object.freeze(["slots", "slotProps"]);
|
|
6
|
+
const PieceSlotsContext = createContext(null);
|
|
7
|
+
function partKeyOf(slot) {
|
|
8
|
+
return `${slot.charAt(0).toLowerCase()}${slot.slice(1)}`;
|
|
9
|
+
}
|
|
10
|
+
function sxListOf(sx) {
|
|
11
|
+
if (sx === void 0 || sx === null) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return Array.isArray(sx) ? sx : [sx];
|
|
15
|
+
}
|
|
16
|
+
function mergedSlotProps(partProps, extra) {
|
|
17
|
+
const extraProps = typeof extra === "function" ? extra(partProps) : extra;
|
|
18
|
+
if (!extraProps) {
|
|
19
|
+
return partProps;
|
|
20
|
+
}
|
|
21
|
+
const className = [partProps.className, extraProps.className].filter(Boolean).join(" ") || void 0;
|
|
22
|
+
const merged = { ...partProps, ...extraProps, className };
|
|
23
|
+
if (partProps.style || extraProps.style) {
|
|
24
|
+
merged.style = { ...partProps.style, ...extraProps.style };
|
|
25
|
+
}
|
|
26
|
+
if (partProps.sx !== void 0 || extraProps.sx !== void 0) {
|
|
27
|
+
merged.sx = [...sxListOf(partProps.sx), ...sxListOf(extraProps.sx)];
|
|
28
|
+
}
|
|
29
|
+
return merged;
|
|
30
|
+
}
|
|
4
31
|
function piecePart(name, slot, component = "div") {
|
|
5
|
-
|
|
32
|
+
const StyledPart = styled(component, { name, slot, shouldForwardProp: (prop) => !PIECE_ONLY_PROPS.has(prop) })(
|
|
6
33
|
({ theme, ownSx }) => ownSx ? theme.unstable_sx(ownSx) : {}
|
|
7
34
|
);
|
|
35
|
+
const partKey = partKeyOf(slot);
|
|
36
|
+
function PiecePart(props) {
|
|
37
|
+
const pieceSlots = useContext(PieceSlotsContext)?.[name];
|
|
38
|
+
if (!pieceSlots) {
|
|
39
|
+
return createElement(StyledPart, props);
|
|
40
|
+
}
|
|
41
|
+
const partProps = mergedSlotProps(props, pieceSlots.slotProps?.[partKey]);
|
|
42
|
+
const Replacement = pieceSlots.slots?.[partKey];
|
|
43
|
+
if (!Replacement) {
|
|
44
|
+
return createElement(StyledPart, partProps);
|
|
45
|
+
}
|
|
46
|
+
const { ownSx: unusedOwnSx, ownerState: unusedOwnerState, ...replacementProps } = partProps;
|
|
47
|
+
return createElement(Replacement, replacementProps);
|
|
48
|
+
}
|
|
49
|
+
PiecePart.displayName = `${name}${slot}`;
|
|
50
|
+
return PiecePart;
|
|
51
|
+
}
|
|
52
|
+
function usePieceProps(props, name, { ownSlotProps = false } = {}) {
|
|
53
|
+
const themedProps = useThemeProps({ props, name });
|
|
54
|
+
if (ownSlotProps || !SLOT_PROP_NAMES.some((propName) => propName in themedProps)) {
|
|
55
|
+
return themedProps;
|
|
56
|
+
}
|
|
57
|
+
const { slots: unusedSlots, slotProps: unusedSlotProps, ...pieceProps } = themedProps;
|
|
58
|
+
return pieceProps;
|
|
8
59
|
}
|
|
9
|
-
function
|
|
10
|
-
|
|
60
|
+
function withPieceSlots(Content, name, { sharedWithParent = false } = {}) {
|
|
61
|
+
function PieceWithSlots(props) {
|
|
62
|
+
const { slots, slotProps } = useThemeProps({ props, name });
|
|
63
|
+
const outerSlots = useContext(PieceSlotsContext);
|
|
64
|
+
const { slots: unusedSlots, slotProps: unusedSlotProps, ...contentProps } = props;
|
|
65
|
+
const usesParentSlots = sharedWithParent && Boolean(outerSlots?.[name]) && !props.slots && !props.slotProps;
|
|
66
|
+
if (usesParentSlots || !slots && !slotProps && !outerSlots?.[name]) {
|
|
67
|
+
return createElement(Content, contentProps);
|
|
68
|
+
}
|
|
69
|
+
const value = { ...outerSlots, [name]: { slots, slotProps } };
|
|
70
|
+
return createElement(PieceSlotsContext.Provider, { value }, createElement(Content, contentProps));
|
|
71
|
+
}
|
|
72
|
+
PieceWithSlots.displayName = (Content.displayName ?? Content.name ?? name).replace(/Content$/, "");
|
|
73
|
+
return PieceWithSlots;
|
|
11
74
|
}
|
|
12
75
|
export {
|
|
76
|
+
mergedSlotProps,
|
|
13
77
|
piecePart,
|
|
14
|
-
|
|
78
|
+
sxListOf,
|
|
79
|
+
usePieceProps,
|
|
80
|
+
withPieceSlots
|
|
15
81
|
};
|
|
16
82
|
//# sourceMappingURL=themePieces.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"themePieces.js","sources":["../../src/theme/themePieces.js"],"sourcesContent":["// Las piezas de Calcita se cambian desde el tema del proyecto como las de MUI: theme.components.Calcita<Pieza> con\n// defaultProps y styleOverrides de cada una de sus partes (root, title, card…). Lo del proyecto va DESPUÉS del estilo de\n// Calcita y le gana; los números se leen como en MUI (borderRadius: 4 son 4 px).\n//\n// Cómo se arma una parte con nombre: piecePart(nombre, parte, componente) da el componente (Box, Paper, Typography…) con su\n// lugar en el tema, y el estilo de Calcita le llega en `ownSx` (lo mismo que antes iba en `sx`, con su forma de sx: objeto,\n// lista o función del tema). Va en la base de la parte, ANTES de lo que ponga el proyecto. OJO: si quedara en `sx`, MUI lo\n// aplicaría al final y el tema del proyecto ya no le podría ganar.\n//\n// const CardRoot = piecePart('CalcitaMetricCard', 'Root', Paper);\n// <CardRoot variant=\"outlined\" ownSx={{p: 2, borderRadius: 3}}>…</CardRoot>\n//\n// Y sus props por omisión: const {label, value} = usePieceProps(props, 'CalcitaMetricCard').\n//\n// Funciones genéricas del paquete: function (regla de la casa).\nimport {styled, useThemeProps} from '@mui/material/styles';\n\n// lo que la parte usa y no se le pasa al componente de adentro (ni al HTML)\nconst PIECE_ONLY_PROPS = new Set(['ownSx', 'ownerState', 'theme', 'as']);\n\n/**\n * Un componente con su lugar en el tema: theme.components[name].styleOverrides[parte con minúscula] se aplica después\n * del estilo de Calcita (ownSx) y gana.\n *\n * @template {import('react').ElementType} C\n * @param {string} name el nombre de la pieza en el tema (CalcitaMetricCard)\n * @param {string} slot la parte, con mayúscula: Root, Title, Card…\n * @param {C} [component] lo que se pinta (por omisión un div)\n * @returns {import('react').ComponentType<any>}\n */\nexport function piecePart(name, slot, component = 'div') {\n return styled(component, {name, slot, shouldForwardProp: (prop) => !PIECE_ONLY_PROPS.has(prop)})(\n ({theme, ownSx}) => (ownSx ? theme.unstable_sx(ownSx) : {}),\n );\n}\n\n/**\n * Los props de la pieza con los defaultProps del tema del proyecto debajo: lo que llega en la pantalla gana.\n *\n * @template {object} P\n * @param {P} props\n * @param {string} name el nombre de la pieza en el tema\n * @returns {P}\n */\nexport function usePieceProps(props, name) {\n return useThemeProps({props, name});\n}\n"],"names":[],"mappings":";;AAkBA,MAAM,mBAAmB,oBAAI,IAAI,CAAC,SAAS,cAAc,SAAS,IAAI,CAAC;AAYhE,SAAS,UAAU,MAAM,MAAM,YAAY,OAAO;AACrD,SAAO,OAAO,WAAW,EAAC,MAAM,MAAM,mBAAmB,CAAC,SAAS,CAAC,iBAAiB,IAAI,IAAI,EAAC,CAAC;AAAA,IAC3F,CAAC,EAAC,OAAO,MAAK,MAAO,QAAQ,MAAM,YAAY,KAAK,IAAI;EAChE;AACA;AAUO,SAAS,cAAc,OAAO,MAAM;AACvC,SAAO,cAAc,EAAC,OAAO,KAAI,CAAC;AACtC;"}
|
|
1
|
+
{"version":3,"file":"themePieces.js","sources":["../../src/theme/themePieces.js"],"sourcesContent":["// Las piezas de Calcita se cambian desde el tema del proyecto como las de MUI: theme.components.Calcita<Pieza> con\n// defaultProps y styleOverrides de cada una de sus partes (root, title, card…). Lo del proyecto va DESPUÉS del estilo de\n// Calcita y le gana; los números se leen como en MUI (borderRadius: 4 son 4 px).\n//\n// Cómo se arma una parte con nombre: piecePart(nombre, parte, componente) da el componente (Box, Paper, Typography…) con su\n// lugar en el tema, y el estilo de Calcita le llega en `ownSx` (lo mismo que antes iba en `sx`, con su forma de sx: objeto,\n// lista o función del tema). Va en la base de la parte, ANTES de lo que ponga el proyecto. OJO: si quedara en `sx`, MUI lo\n// aplicaría al final y el tema del proyecto ya no le podría ganar.\n//\n// const CardRoot = piecePart('CalcitaMetricCard', 'Root', Paper);\n// <CardRoot variant=\"outlined\" ownSx={{p: 2, borderRadius: 3}}>…</CardRoot>\n//\n// Y sus props por omisión: const {label, value} = usePieceProps(props, 'CalcitaMetricCard').\n//\n// SLOTS, como en MUI: cada parte con nombre también se puede cambiar por otro componente (`slots`) o recibir props de más\n// (`slotProps`), con el nombre de la parte en minúscula, el mismo de styleOverrides. Desde la pantalla o desde el tema\n// (defaultProps), sin pedirle nada a Calcita:\n//\n// <RecordCard slots={{avatar: MiAvatar}} slotProps={{title: {component: 'h2'}}} … />\n// components: {CalcitaRecordCard: {defaultProps: {slotProps: {root: {elevation: 2}}}}}\n//\n// Cada pieza se exporta envuelta con withPieceSlots(Contenido, nombre): la envoltura lee slots y slotProps y se los da a\n// sus partes por contexto (de cada pieza el suyo: una pieza dentro de otra no se los roba).\n//\n// Funciones genéricas del paquete: function (regla de la casa).\nimport {createContext, createElement, useContext} from 'react';\nimport {styled, useThemeProps} from '@mui/material/styles';\n\n// lo que la parte usa y no se le pasa al componente de adentro (ni al HTML)\nconst PIECE_ONLY_PROPS = new Set(['ownSx', 'ownerState', 'theme', 'as']);\n// lo que la pieza recibe para sus partes: no le llega al contenido (no son props suyas)\nconst SLOT_PROP_NAMES = Object.freeze(['slots', 'slotProps']);\n\n// pieza → {slots, slotProps} de la que se está pintando (y de las de afuera)\nconst PieceSlotsContext = createContext(null);\n\n/** El nombre de la parte como va en el tema y en slots: 'Root' → 'root', 'ItemIcon' → 'itemIcon'. */\nfunction partKeyOf(slot) {\n return `${slot.charAt(0).toLowerCase()}${slot.slice(1)}`;\n}\n\n/**\n * El sx de la pantalla como lista, para ponerlo DESPUÉS del de la pieza sin mezclarlo dentro del mismo objeto.\n *\n * OJO, trampa pagada: `{...estiloDeLaPieza, ...sx}` reemplaza la llave entera cuando las dos traen la misma\n * ('& .MuiDataGrid-cell': la pieza ponía display flex y la pantalla solo quería alinear arriba: se perdía el flex sin\n * avisar). En una lista, MUI aplica cada elemento encima del anterior y se combinan regla por regla.\n *\n * sx={[{p: 2}, ...sxListOf(sx)]}\n *\n * @param {object|Function|Array<object|Function|boolean|null|undefined>|null|undefined} sx\n * @returns {Array<object|Function|boolean|null|undefined>}\n */\nexport function sxListOf(sx) {\n if (sx === undefined || sx === null) {\n return [];\n }\n return Array.isArray(sx) ? sx : [sx];\n}\n\n/**\n * Las props de una parte con las de slotProps encima: las de la pantalla ganan, menos className (se suman), style (se\n * juntan) y sx (va después y se combina regla por regla).\n *\n * @param {object} partProps las que le pone Calcita\n * @param {object|((partProps: object) => object)|undefined} extra las de slotProps (o una función que las da)\n * @returns {object}\n */\nexport function mergedSlotProps(partProps, extra) {\n const extraProps = typeof extra === 'function' ? extra(partProps) : extra;\n if (!extraProps) {\n return partProps;\n }\n const className = [partProps.className, extraProps.className].filter(Boolean).join(' ') || undefined;\n const merged = {...partProps, ...extraProps, className};\n if (partProps.style || extraProps.style) {\n merged.style = {...partProps.style, ...extraProps.style};\n }\n if (partProps.sx !== undefined || extraProps.sx !== undefined) {\n merged.sx = [...sxListOf(partProps.sx), ...sxListOf(extraProps.sx)];\n }\n return merged;\n}\n\n/**\n * Un componente con su lugar en el tema: theme.components[name].styleOverrides[parte con minúscula] se aplica después\n * del estilo de Calcita (ownSx) y gana. La parte también se puede cambiar o recibir props de más con slots y slotProps\n * de su pieza.\n *\n * @template {import('react').ElementType} C\n * @param {string} name el nombre de la pieza en el tema (CalcitaMetricCard)\n * @param {string} slot la parte, con mayúscula: Root, Title, Card…\n * @param {C} [component] lo que se pinta (por omisión un div)\n * @returns {import('react').ComponentType<any>}\n */\nexport function piecePart(name, slot, component = 'div') {\n const StyledPart = styled(component, {name, slot, shouldForwardProp: (prop) => !PIECE_ONLY_PROPS.has(prop)})(\n ({theme, ownSx}) => (ownSx ? theme.unstable_sx(ownSx) : {}),\n );\n const partKey = partKeyOf(slot);\n\n // React 19: ref llega como una prop más y se pasa tal cual\n function PiecePart(props) {\n const pieceSlots = useContext(PieceSlotsContext)?.[name];\n if (!pieceSlots) {\n return createElement(StyledPart, props);\n }\n const partProps = mergedSlotProps(props, pieceSlots.slotProps?.[partKey]);\n const Replacement = pieceSlots.slots?.[partKey];\n if (!Replacement) {\n return createElement(StyledPart, partProps);\n }\n // el componente de la pantalla no sabe de los estilos de Calcita: le llega lo demás\n const {ownSx: unusedOwnSx, ownerState: unusedOwnerState, ...replacementProps} = partProps;\n return createElement(Replacement, replacementProps);\n }\n PiecePart.displayName = `${name}${slot}`;\n return PiecePart;\n}\n\n/**\n * Los props de la pieza con los defaultProps del tema del proyecto debajo: lo que llega en la pantalla gana. Sin slots ni\n * slotProps: esos son de sus partes (withPieceSlots).\n *\n * OJO: una pieza cuyo `slotProps` ya es de otra cosa (TextInput se lo pasa al TextField de MUI: input, htmlInput…) lo\n * conserva con {ownSlotProps: true} y no se envuelve con withPieceSlots.\n *\n * @template {object} P\n * @param {P} props\n * @param {string} name el nombre de la pieza en el tema\n * @param {{ownSlotProps?: boolean}} [options]\n * @returns {P}\n */\nexport function usePieceProps(props, name, {ownSlotProps = false} = {}) {\n const themedProps = useThemeProps({props, name});\n if (ownSlotProps || !SLOT_PROP_NAMES.some((propName) => propName in themedProps)) {\n return themedProps;\n }\n const {slots: unusedSlots, slotProps: unusedSlotProps, ...pieceProps} = themedProps;\n return pieceProps;\n}\n\n/**\n * La pieza con slots y slotProps: lee los de la pantalla (y los de defaultProps del tema) y se los da a sus partes.\n *\n * OJO con el tipo: se toma del componente entero (Parameters) y no de un genérico de sus props: con las props opcionales\n * (ColorSchemeToggle) TypeScript las deducía como `object` y se perdían.\n *\n * Una parte que también se exporta sola y se pinta DENTRO de su pieza madre con el mismo nombre (las tarjetas de\n * TableCardsView dentro de ServerTable, las dos CalcitaServerTable) va con {sharedWithParent: true}: adentro usa los slots\n * de la madre (sin eso los borraría con los suyos, vacíos), salvo que la pantalla le dé los suyos; sola, los suyos.\n *\n * @template {(props: any) => any} C\n * @param {C} Content la pieza\n * @param {string} name el nombre de la pieza en el tema\n * @param {{sharedWithParent?: boolean}} [options]\n * @returns {(props: NonNullable<Parameters<C>[0]> & {slots?: Record<string, import('react').ElementType>, slotProps?: Record<string, object|((partProps: object) => object)>}) => ReturnType<C>}\n */\nexport function withPieceSlots(Content, name, {sharedWithParent = false} = {}) {\n function PieceWithSlots(props) {\n const {slots, slotProps} = useThemeProps({props, name});\n const outerSlots = useContext(PieceSlotsContext);\n // slots y slotProps son de las partes, no de la pieza: no le llegan (una pieza que avisa de props que no conoce\n // los tomaría por un error de la pantalla)\n const {slots: unusedSlots, slotProps: unusedSlotProps, ...contentProps} = props;\n // dentro de su madre con el mismo nombre se queda con los de ella, salvo que la pantalla le dé los suyos\n const usesParentSlots = sharedWithParent && Boolean(outerSlots?.[name]) && !props.slots && !props.slotProps;\n // sin slots ni slotProps (lo de casi siempre) no se agrega nada: la pieza se pinta igual que antes\n if (usesParentSlots || (!slots && !slotProps && !outerSlots?.[name])) {\n return createElement(Content, contentProps);\n }\n // la de adentro con el mismo nombre (una tarjeta dentro de otra) no hereda los de afuera\n const value = {...outerSlots, [name]: {slots, slotProps}};\n return createElement(PieceSlotsContext.Provider, {value}, createElement(Content, contentProps));\n }\n PieceWithSlots.displayName = (Content.displayName ?? Content.name ?? name).replace(/Content$/, '');\n return PieceWithSlots;\n}\n"],"names":[],"mappings":";;;AA6BA,MAAM,mBAAmB,oBAAI,IAAI,CAAC,SAAS,cAAc,SAAS,IAAI,CAAC;AAEvE,MAAM,kBAAkB,OAAO,OAAO,CAAC,SAAS,WAAW,CAAC;AAG5D,MAAM,oBAAoB,cAAc,IAAI;AAG5C,SAAS,UAAU,MAAM;AACrB,SAAO,GAAG,KAAK,OAAO,CAAC,EAAE,YAAW,CAAE,GAAG,KAAK,MAAM,CAAC,CAAC;AAC1D;AAcO,SAAS,SAAS,IAAI;AACzB,MAAI,OAAO,UAAa,OAAO,MAAM;AACjC,WAAO,CAAA;AAAA,EACX;AACA,SAAO,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE;AACvC;AAUO,SAAS,gBAAgB,WAAW,OAAO;AAC9C,QAAM,aAAa,OAAO,UAAU,aAAa,MAAM,SAAS,IAAI;AACpE,MAAI,CAAC,YAAY;AACb,WAAO;AAAA,EACX;AACA,QAAM,YAAY,CAAC,UAAU,WAAW,WAAW,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK;AAC3F,QAAM,SAAS,EAAC,GAAG,WAAW,GAAG,YAAY,UAAS;AACtD,MAAI,UAAU,SAAS,WAAW,OAAO;AACrC,WAAO,QAAQ,EAAC,GAAG,UAAU,OAAO,GAAG,WAAW,MAAK;AAAA,EAC3D;AACA,MAAI,UAAU,OAAO,UAAa,WAAW,OAAO,QAAW;AAC3D,WAAO,KAAK,CAAC,GAAG,SAAS,UAAU,EAAE,GAAG,GAAG,SAAS,WAAW,EAAE,CAAC;AAAA,EACtE;AACA,SAAO;AACX;AAaO,SAAS,UAAU,MAAM,MAAM,YAAY,OAAO;AACrD,QAAM,aAAa,OAAO,WAAW,EAAC,MAAM,MAAM,mBAAmB,CAAC,SAAS,CAAC,iBAAiB,IAAI,IAAI,EAAC,CAAC;AAAA,IACvG,CAAC,EAAC,OAAO,MAAK,MAAO,QAAQ,MAAM,YAAY,KAAK,IAAI;EAChE;AACI,QAAM,UAAU,UAAU,IAAI;AAG9B,WAAS,UAAU,OAAO;AACtB,UAAM,aAAa,WAAW,iBAAiB,IAAI,IAAI;AACvD,QAAI,CAAC,YAAY;AACb,aAAO,cAAc,YAAY,KAAK;AAAA,IAC1C;AACA,UAAM,YAAY,gBAAgB,OAAO,WAAW,YAAY,OAAO,CAAC;AACxE,UAAM,cAAc,WAAW,QAAQ,OAAO;AAC9C,QAAI,CAAC,aAAa;AACd,aAAO,cAAc,YAAY,SAAS;AAAA,IAC9C;AAEA,UAAM,EAAC,OAAO,aAAa,YAAY,kBAAkB,GAAG,iBAAgB,IAAI;AAChF,WAAO,cAAc,aAAa,gBAAgB;AAAA,EACtD;AACA,YAAU,cAAc,GAAG,IAAI,GAAG,IAAI;AACtC,SAAO;AACX;AAeO,SAAS,cAAc,OAAO,MAAM,EAAC,eAAe,MAAK,IAAI,IAAI;AACpE,QAAM,cAAc,cAAc,EAAC,OAAO,KAAI,CAAC;AAC/C,MAAI,gBAAgB,CAAC,gBAAgB,KAAK,CAAC,aAAa,YAAY,WAAW,GAAG;AAC9E,WAAO;AAAA,EACX;AACA,QAAM,EAAC,OAAO,aAAa,WAAW,iBAAiB,GAAG,WAAU,IAAI;AACxE,SAAO;AACX;AAkBO,SAAS,eAAe,SAAS,MAAM,EAAC,mBAAmB,MAAK,IAAI,IAAI;AAC3E,WAAS,eAAe,OAAO;AAC3B,UAAM,EAAC,OAAO,UAAS,IAAI,cAAc,EAAC,OAAO,KAAI,CAAC;AACtD,UAAM,aAAa,WAAW,iBAAiB;AAG/C,UAAM,EAAC,OAAO,aAAa,WAAW,iBAAiB,GAAG,aAAY,IAAI;AAE1E,UAAM,kBAAkB,oBAAoB,QAAQ,aAAa,IAAI,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC,MAAM;AAElG,QAAI,mBAAoB,CAAC,SAAS,CAAC,aAAa,CAAC,aAAa,IAAI,GAAI;AAClE,aAAO,cAAc,SAAS,YAAY;AAAA,IAC9C;AAEA,UAAM,QAAQ,EAAC,GAAG,YAAY,CAAC,IAAI,GAAG,EAAC,OAAO,UAAS,EAAC;AACxD,WAAO,cAAc,kBAAkB,UAAU,EAAC,MAAK,GAAG,cAAc,SAAS,YAAY,CAAC;AAAA,EAClG;AACA,iBAAe,eAAe,QAAQ,eAAe,QAAQ,QAAQ,MAAM,QAAQ,YAAY,EAAE;AACjG,SAAO;AACX;"}
|
|
@@ -11,6 +11,8 @@ const ToolkitProvider = ({
|
|
|
11
11
|
language,
|
|
12
12
|
languages,
|
|
13
13
|
onLanguageChange,
|
|
14
|
+
onMissingText,
|
|
15
|
+
readValidationMessage,
|
|
14
16
|
texts,
|
|
15
17
|
translate,
|
|
16
18
|
timeZone,
|
|
@@ -30,6 +32,8 @@ const ToolkitProvider = ({
|
|
|
30
32
|
language: language ?? parent?.language ?? DEFAULT_LANGUAGE,
|
|
31
33
|
languages: languages ?? parent?.languages ?? DEFAULT_LANGUAGES,
|
|
32
34
|
onLanguageChange: onLanguageChange ?? parent?.onLanguageChange ?? null,
|
|
35
|
+
onMissingText: onMissingText ?? parent?.onMissingText ?? null,
|
|
36
|
+
readValidationMessage: readValidationMessage ?? parent?.readValidationMessage ?? null,
|
|
33
37
|
texts: texts ?? parent?.texts ?? {},
|
|
34
38
|
translate: translate ?? parent?.translate ?? null,
|
|
35
39
|
timeZone: timeZone ?? parent?.timeZone ?? browserTimeZone(),
|
|
@@ -45,6 +49,8 @@ const ToolkitProvider = ({
|
|
|
45
49
|
language,
|
|
46
50
|
languages,
|
|
47
51
|
onLanguageChange,
|
|
52
|
+
onMissingText,
|
|
53
|
+
readValidationMessage,
|
|
48
54
|
texts,
|
|
49
55
|
translate,
|
|
50
56
|
timeZone,
|
|
@@ -66,7 +72,8 @@ const ToolkitProvider = ({
|
|
|
66
72
|
hourCycle: settings.hourCycle,
|
|
67
73
|
appName: settings.appName,
|
|
68
74
|
buildId: settings.buildId,
|
|
69
|
-
languages: settings.languages
|
|
75
|
+
languages: settings.languages,
|
|
76
|
+
onMissingText: settings.onMissingText
|
|
70
77
|
});
|
|
71
78
|
}
|
|
72
79
|
useFontSize();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolkitProvider.js","sources":["../../src/toolkit/ToolkitProvider.jsx"],"sourcesContent":["// El proveedor del paquete: el único lugar donde cada aplicación conecta su idioma, sus textos, su zona horaria y su\n// router. Va por encima de la aplicación, dentro del ThemeProvider de MUI (el tema lo arma cada proyecto; sin tema,\n// los componentes usan los colores por omisión de MUI).\n//\n// <ToolkitProvider language=\"es\" navigation={reactRouterNavigation} texts={{es: {common: {actions: {back: 'Volver'}}}}}>\n//\n// Sin proveedor todo funciona en español, con la zona del navegador y la navegación del navegador.\n//\n// Un proveedor dentro de otro (una parte de la app en otro idioma, una vista previa) toma del de afuera lo que no le digan, y\n// sus componentes leen el suyo: el más cercano. Solo el de la raíz pone la configuración que leen las funciones sueltas\n// (avisos, errores, formatos sueltos): antes el de adentro se la cambiaba a toda la app. Dos islas de React en una página\n// vieja son dos raíces: cada isla pinta con la suya, y las funciones sueltas usan la última que se pintó (en ese caso los\n// componentes de la casa ya formatean con su proveedor; para un aviso, mándelo con el texto ya traducido).\n//\n// Lo renderiza la app: arrow (regla de la casa).\nimport {useContext, useMemo} from 'react';\nimport {allowOnlyUnrestricted} from '@/access/accessRules.js';\nimport {browserNavigation} from '@/toolkit/browserNavigation.js';\nimport {ToolkitContext} from '@/toolkit/toolkitContext.js';\nimport {useFontSize} from '@/ui/navigation/useFontSize.js';\nimport {DEFAULT_CURRENCY, DEFAULT_HOUR_CYCLE, DEFAULT_LANGUAGE, DEFAULT_LANGUAGES, browserTimeZone, configureToolkit} from '@/toolkit/toolkitSettings.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/**\n * @param {object} props\n * @param {string} [props.language] idioma activo, uno de `languages` (por omisión es)\n * @param {Array<{code: string, label: string, locale?: string}>} [props.languages] los idiomas que ofrece la app: su código,\n * su nombre en su propio idioma («Português») y su locale de Intl ('pt-BR'). Por omisión español e inglés\n * (DEFAULT_LANGUAGES). Sus traducciones van en `texts`\n * @param {Record<string, {Icon: import('react').ElementType, color?: string, label?: string, hint?: string}>} [props.fileTypes]\n * tipos de archivo del proyecto para las descargas, además de Excel, CSV y PDF (downloads/fileTypes.js)\n * @param {(language: string) => void} [props.onLanguageChange] lo llama LanguageToggle; el proyecto cambia su idioma\n * @param {object} [props.texts] textos propios por idioma, con la forma de locales/ ({es: {table: {...}}})\n * @param {(namespace: string, key: string, values?: object) => string|undefined} [props.translate] traductor del\n * proyecto (por ejemplo i18next); undefined deja el texto del paquete\n * @param {string} [props.timeZone] zona de las fechas (por omisión la del navegador)\n * @param {string} [props.currency] moneda de los montos, código ISO 4217 (por omisión MXN)\n * @param {'h23'|'h12'} [props.hourCycle] la hora de 24 horas («15:10», por omisión) o de 12 con a.m. y p.m. («3:10 p.m.»):\n * HOUR_CYCLE.twentyFourHours o HOUR_CYCLE.twelveHours\n * @param {string} [props.appName] nombre de la aplicación para el título de la pestaña\n * @param {object} [props.navigation] {useNavigate, useLocation, Link}; por omisión la del navegador\n * @param {string} [props.buildId] la compilación del front que corre en esta pestaña; con ella NewBuildNotice sabe si el\n * servidor ya publicó otra (reportPublishedBuild). Vacía (en desarrollo) nunca avisa\n * @param {(access: object|undefined|null) => boolean} [props.canAccess] quién ve qué (menú, rutas, botones): la regla del\n * proyecto, normalmente createAccessChecker({abilities, roles, policies, context}). Sin ella solo se ve lo que no\n * tiene regla\n * @param {import('react').ReactNode} props.children\n */\nexport const ToolkitProvider = ({\n language, languages, onLanguageChange, texts, translate, timeZone, currency, hourCycle, appName, navigation, buildId, canAccess, fileTypes,\n children,\n ...unknownProps\n}) => {\n warnUnknownProps('ToolkitProvider', unknownProps);\n // el proveedor de afuera, si este va dentro de otro: de él sale lo que este no diga\n const parent = useContext(ToolkitContext);\n const settings = useMemo(() => ({\n language: language ?? parent?.language ?? DEFAULT_LANGUAGE,\n languages: languages ?? parent?.languages ?? DEFAULT_LANGUAGES,\n onLanguageChange: onLanguageChange ?? parent?.onLanguageChange ?? null,\n texts: texts ?? parent?.texts ?? {},\n translate: translate ?? parent?.translate ?? null,\n timeZone: timeZone ?? parent?.timeZone ?? browserTimeZone(),\n currency: currency ?? parent?.currency ?? DEFAULT_CURRENCY,\n hourCycle: hourCycle ?? parent?.hourCycle ?? DEFAULT_HOUR_CYCLE,\n appName: appName ?? parent?.appName ?? '',\n navigation: navigation ?? parent?.navigation ?? browserNavigation,\n buildId: buildId ?? parent?.buildId ?? '',\n canAccess: canAccess ?? parent?.canAccess ?? allowOnlyUnrestricted,\n fileTypes: fileTypes ?? parent?.fileTypes ?? null,\n }), [parent, language, languages, onLanguageChange, texts, translate, timeZone, currency, hourCycle, appName, navigation, buildId, canAccess,\n fileTypes]);\n\n // solo el de la raíz: un proveedor de adentro le cambiaba el idioma y la zona a las funciones sueltas de toda la app.\n // OJO: se configura durante el render (no en un efecto) para que los formatos y avisos que se llaman en este mismo\n // render ya vean el idioma y la zona nuevos\n if (!parent) {\n configureToolkit({\n language: settings.language, texts: settings.texts, translate: settings.translate, timeZone: settings.timeZone, currency: settings.currency,\n hourCycle: settings.hourCycle, appName: settings.appName, buildId: settings.buildId, languages: settings.languages,\n });\n }\n\n // el tamaño de letra que la persona guardó: se aplica aquí, que siempre está montado. Antes lo aplicaba su botón en la\n // barra; al pasar los ajustes al menú de la cuenta, al recargar se perdía hasta abrir el menú (error pagado)\n useFontSize();\n\n return <ToolkitContext.Provider value={settings}>{children}</ToolkitContext.Provider>;\n};\n"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"ToolkitProvider.js","sources":["../../src/toolkit/ToolkitProvider.jsx"],"sourcesContent":["// El proveedor del paquete: el único lugar donde cada aplicación conecta su idioma, sus textos, su zona horaria y su\n// router. Va por encima de la aplicación, dentro del ThemeProvider de MUI (el tema lo arma cada proyecto; sin tema,\n// los componentes usan los colores por omisión de MUI).\n//\n// <ToolkitProvider language=\"es\" navigation={reactRouterNavigation} texts={{es: {common: {actions: {back: 'Volver'}}}}}>\n//\n// Sin proveedor todo funciona en español, con la zona del navegador y la navegación del navegador.\n//\n// Un proveedor dentro de otro (una parte de la app en otro idioma, una vista previa) toma del de afuera lo que no le digan, y\n// sus componentes leen el suyo: el más cercano. Solo el de la raíz pone la configuración que leen las funciones sueltas\n// (avisos, errores, formatos sueltos): antes el de adentro se la cambiaba a toda la app. Dos islas de React en una página\n// vieja son dos raíces: cada isla pinta con la suya, y las funciones sueltas usan la última que se pintó (en ese caso los\n// componentes de la casa ya formatean con su proveedor; para un aviso, mándelo con el texto ya traducido).\n//\n// Lo renderiza la app: arrow (regla de la casa).\nimport {useContext, useMemo} from 'react';\nimport {allowOnlyUnrestricted} from '@/access/accessRules.js';\nimport {browserNavigation} from '@/toolkit/browserNavigation.js';\nimport {ToolkitContext} from '@/toolkit/toolkitContext.js';\nimport {useFontSize} from '@/ui/navigation/useFontSize.js';\nimport {DEFAULT_CURRENCY, DEFAULT_HOUR_CYCLE, DEFAULT_LANGUAGE, DEFAULT_LANGUAGES, browserTimeZone, configureToolkit} from '@/toolkit/toolkitSettings.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/**\n * @param {object} props\n * @param {string} [props.language] idioma activo, uno de `languages` (por omisión es)\n * @param {Array<{code: string, label: string, locale?: string}>} [props.languages] los idiomas que ofrece la app: su código,\n * su nombre en su propio idioma («Português») y su locale de Intl ('pt-BR'). Por omisión español e inglés\n * (DEFAULT_LANGUAGES). Sus traducciones van en `texts`\n * @param {Record<string, {Icon: import('react').ElementType, color?: string, label?: string, hint?: string}>} [props.fileTypes]\n * tipos de archivo del proyecto para las descargas, además de Excel, CSV y PDF (downloads/fileTypes.js)\n * @param {(language: string) => void} [props.onLanguageChange] lo llama LanguageToggle; el proyecto cambia su idioma\n * @param {object} [props.texts] textos propios por idioma, con la forma de locales/ ({es: {table: {...}}})\n * @param {(error: unknown) => unknown} [props.readValidationMessage] cómo leer los mensajes de validación de un sistema que ya\n * existe (su propio setLocale de yup, su forma de error): regresa un texto, {key, values} o undefined para la lectura de\n * siempre. Ej. ({msg, values}) de muchos sistemas con i18next ya se entiende solo; esto es para lo demás\n * @param {(missing: {namespace: string, key: string, language: string}) => void} [props.onMissingText] avisa cada texto que\n * no existe (la pantalla enseña la llave): para mandarlo a Sentry o tronar una prueba. Sin él, Calcita avisa en la\n * consola una vez por idioma y llave\n * @param {(namespace: string, key: string, values?: object) => string|undefined} [props.translate] traductor del\n * proyecto (por ejemplo i18next); undefined deja el texto del paquete\n * @param {string} [props.timeZone] zona de las fechas (por omisión la del navegador)\n * @param {string} [props.currency] moneda de los montos, código ISO 4217 (por omisión MXN)\n * @param {'h23'|'h12'} [props.hourCycle] la hora de 24 horas («15:10», por omisión) o de 12 con a.m. y p.m. («3:10 p.m.»):\n * HOUR_CYCLE.twentyFourHours o HOUR_CYCLE.twelveHours\n * @param {string} [props.appName] nombre de la aplicación para el título de la pestaña\n * @param {object} [props.navigation] {useNavigate, useLocation, Link}; por omisión la del navegador\n * @param {string} [props.buildId] la compilación del front que corre en esta pestaña; con ella NewBuildNotice sabe si el\n * servidor ya publicó otra (reportPublishedBuild). Vacía (en desarrollo) nunca avisa\n * @param {(access: object|undefined|null) => boolean} [props.canAccess] quién ve qué (menú, rutas, botones): la regla del\n * proyecto, normalmente createAccessChecker({abilities, roles, policies, context}). Sin ella solo se ve lo que no\n * tiene regla\n * @param {import('react').ReactNode} props.children\n */\nexport const ToolkitProvider = ({\n language, languages, onLanguageChange, onMissingText, readValidationMessage, texts, translate, timeZone, currency, hourCycle, appName, navigation, buildId, canAccess, fileTypes,\n children,\n ...unknownProps\n}) => {\n warnUnknownProps('ToolkitProvider', unknownProps);\n // el proveedor de afuera, si este va dentro de otro: de él sale lo que este no diga\n const parent = useContext(ToolkitContext);\n const settings = useMemo(() => ({\n language: language ?? parent?.language ?? DEFAULT_LANGUAGE,\n languages: languages ?? parent?.languages ?? DEFAULT_LANGUAGES,\n onLanguageChange: onLanguageChange ?? parent?.onLanguageChange ?? null,\n onMissingText: onMissingText ?? parent?.onMissingText ?? null,\n readValidationMessage: readValidationMessage ?? parent?.readValidationMessage ?? null,\n texts: texts ?? parent?.texts ?? {},\n translate: translate ?? parent?.translate ?? null,\n timeZone: timeZone ?? parent?.timeZone ?? browserTimeZone(),\n currency: currency ?? parent?.currency ?? DEFAULT_CURRENCY,\n hourCycle: hourCycle ?? parent?.hourCycle ?? DEFAULT_HOUR_CYCLE,\n appName: appName ?? parent?.appName ?? '',\n navigation: navigation ?? parent?.navigation ?? browserNavigation,\n buildId: buildId ?? parent?.buildId ?? '',\n canAccess: canAccess ?? parent?.canAccess ?? allowOnlyUnrestricted,\n fileTypes: fileTypes ?? parent?.fileTypes ?? null,\n }), [parent, language, languages, onLanguageChange, onMissingText, readValidationMessage, texts, translate, timeZone, currency, hourCycle, appName, navigation, buildId, canAccess,\n fileTypes]);\n\n // solo el de la raíz: un proveedor de adentro le cambiaba el idioma y la zona a las funciones sueltas de toda la app.\n // OJO: se configura durante el render (no en un efecto) para que los formatos y avisos que se llaman en este mismo\n // render ya vean el idioma y la zona nuevos\n if (!parent) {\n configureToolkit({\n language: settings.language, texts: settings.texts, translate: settings.translate, timeZone: settings.timeZone, currency: settings.currency,\n hourCycle: settings.hourCycle, appName: settings.appName, buildId: settings.buildId, languages: settings.languages,\n onMissingText: settings.onMissingText,\n });\n }\n\n // el tamaño de letra que la persona guardó: se aplica aquí, que siempre está montado. Antes lo aplicaba su botón en la\n // barra; al pasar los ajustes al menú de la cuenta, al recargar se perdía hasta abrir el menú (error pagado)\n useFontSize();\n\n return <ToolkitContext.Provider value={settings}>{children}</ToolkitContext.Provider>;\n};\n"],"names":[],"mappings":";;;;;;;;;AAsDO,MAAM,kBAAkB,CAAC;AAAA,EAC5B;AAAA,EAAU;AAAA,EAAW;AAAA,EAAkB;AAAA,EAAe;AAAA,EAAuB;AAAA,EAAO;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAS;AAAA,EAAY;AAAA,EAAS;AAAA,EAAW;AAAA,EACvK;AAAA,EACA,GAAG;AACP,MAAM;AACF,mBAAiB,mBAAmB,YAAY;AAEhD,QAAM,SAAS,WAAW,cAAc;AACxC,QAAM,WAAW,QAAQ,OAAO;AAAA,IAC5B,UAAU,YAAY,QAAQ,YAAY;AAAA,IAC1C,WAAW,aAAa,QAAQ,aAAa;AAAA,IAC7C,kBAAkB,oBAAoB,QAAQ,oBAAoB;AAAA,IAClE,eAAe,iBAAiB,QAAQ,iBAAiB;AAAA,IACzD,uBAAuB,yBAAyB,QAAQ,yBAAyB;AAAA,IACjF,OAAO,SAAS,QAAQ,SAAS,CAAA;AAAA,IACjC,WAAW,aAAa,QAAQ,aAAa;AAAA,IAC7C,UAAU,YAAY,QAAQ,YAAY,gBAAA;AAAA,IAC1C,UAAU,YAAY,QAAQ,YAAY;AAAA,IAC1C,WAAW,aAAa,QAAQ,aAAa;AAAA,IAC7C,SAAS,WAAW,QAAQ,WAAW;AAAA,IACvC,YAAY,cAAc,QAAQ,cAAc;AAAA,IAChD,SAAS,WAAW,QAAQ,WAAW;AAAA,IACvC,WAAW,aAAa,QAAQ,aAAa;AAAA,IAC7C,WAAW,aAAa,QAAQ,aAAa;AAAA,EAAA,IAC7C;AAAA,IAAC;AAAA,IAAQ;AAAA,IAAU;AAAA,IAAW;AAAA,IAAkB;AAAA,IAAe;AAAA,IAAuB;AAAA,IAAO;AAAA,IAAW;AAAA,IAAU;AAAA,IAAU;AAAA,IAAW;AAAA,IAAS;AAAA,IAAY;AAAA,IAAS;AAAA,IACrK;AAAA,EAAA,CAAU;AAKd,MAAI,CAAC,QAAQ;AACT,qBAAiB;AAAA,MACb,UAAU,SAAS;AAAA,MAAU,OAAO,SAAS;AAAA,MAAO,WAAW,SAAS;AAAA,MAAW,UAAU,SAAS;AAAA,MAAU,UAAU,SAAS;AAAA,MACnI,WAAW,SAAS;AAAA,MAAW,SAAS,SAAS;AAAA,MAAS,SAAS,SAAS;AAAA,MAAS,WAAW,SAAS;AAAA,MACzG,eAAe,SAAS;AAAA,IAAA,CAC3B;AAAA,EACL;AAIA,cAAA;AAEA,6BAAQ,eAAe,UAAf,EAAwB,OAAO,UAAW,UAAS;AAC/D;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createElement, useSyncExternalStore } from "react";
|
|
3
|
-
import { pathWithBase, pathWithoutBase } from "./navigationPaths.js";
|
|
3
|
+
import { pathWithBase, pathWithoutBase, isPlainLeftClick } from "./navigationPaths.js";
|
|
4
4
|
const HISTORY_BACK = -1;
|
|
5
5
|
function subscribeToHistory(onChange) {
|
|
6
6
|
window.addEventListener("popstate", onChange);
|
|
@@ -10,19 +10,34 @@ function currentHref() {
|
|
|
10
10
|
return window.location.href;
|
|
11
11
|
}
|
|
12
12
|
function createBrowserNavigation({ basePath = "" } = {}) {
|
|
13
|
-
const navigate = (destination) => {
|
|
13
|
+
const navigate = (destination, options) => {
|
|
14
14
|
if (destination === HISTORY_BACK) {
|
|
15
15
|
window.history.back();
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
const href = pathWithBase(basePath, destination);
|
|
19
|
+
if (options?.replace) {
|
|
20
|
+
window.location.replace(href);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
window.location.assign(href);
|
|
19
24
|
};
|
|
20
25
|
const useBrowserLocation = () => {
|
|
21
26
|
const href = useSyncExternalStore(subscribeToHistory, currentHref, () => "");
|
|
22
27
|
const url = href ? new URL(href) : null;
|
|
23
28
|
return { pathname: url ? pathWithoutBase(basePath, url.pathname) : "/", search: url?.search ?? "", key: href };
|
|
24
29
|
};
|
|
25
|
-
const BrowserLink = ({ to, ...props }) =>
|
|
30
|
+
const BrowserLink = ({ to, replace, onClick, ...props }) => {
|
|
31
|
+
const href = pathWithBase(basePath, to);
|
|
32
|
+
const handleClick = (event) => {
|
|
33
|
+
onClick?.(event);
|
|
34
|
+
if (replace && isPlainLeftClick(event, props.target)) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
window.location.replace(href);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
return createElement("a", { href, ...props, onClick: handleClick });
|
|
40
|
+
};
|
|
26
41
|
return Object.freeze({ useNavigate: () => navigate, useLocation: useBrowserLocation, Link: BrowserLink });
|
|
27
42
|
}
|
|
28
43
|
const browserNavigation = createBrowserNavigation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browserNavigation.js","sources":["../../src/toolkit/browserNavigation.js"],"sourcesContent":["// La navegación por omisión: la del navegador, para usar el paquete sin router.\n//\n// Los componentes que navegan (BackButton, MetricCard, el regreso a la última lista) no importan react-router ni\n// Next.js: le piden al proveedor una navegación con tres piezas, las mismas que trae react-router:\n// useNavigate() regresa navigate(destino), donde destino es una ruta o -1 para regresar;\n// useLocation() regresa {pathname, search, key} de la página actual;\n// Link el enlace, que recibe `to` con la ruta.\n// Un proyecto con router pasa su adaptador (@stackcraftmx/calcita/react-router); sin él, esta cambia de página como un\n// enlace normal.\n//\n// Para un sistema que ya existe, sin router:\n// - con prefijo (la app detrás de un proxy en /portal): createBrowserNavigation({basePath: '/portal'}). Los componentes\n// siguen hablando de '/pedidos' y la dirección real lleva el prefijo;\n// - con # en la dirección (un servidor que no sabe responder /pedidos al recargar): createHashNavigation(), en\n// hashNavigation.js.\n//\n// OJO: aquí cada navegación recarga la página. Para una aplicación de una sola página se usa el adaptador de su router (o la\n// de #, que no recarga).\nimport {createElement, useSyncExternalStore} from 'react';\n\nimport {pathWithBase, pathWithoutBase} from '@/toolkit/navigationPaths.js';\n\nconst HISTORY_BACK = -1;\n\n/**\n * Avisa cuando cambia la página por el historial (atrás y adelante).\n *\n * @param {Function} onChange\n * @returns {Function} para dejar de escuchar\n */\nfunction subscribeToHistory(onChange) {\n window.addEventListener('popstate', onChange);\n\n return () => window.removeEventListener('popstate', onChange);\n}\n\n/**\n * La dirección actual como texto, para saber si cambió.\n *\n * @returns {string}\n */\nfunction currentHref() {\n return window.location.href;\n}\n\n/**\n * La navegación del navegador, con o sin prefijo.\n *\n * @param {{basePath?: string}} [options] basePath: donde vive la app detrás de un proxy ('/portal'); sin él, la raíz\n * @returns {{useNavigate: () => (destination: string|number) => void, useLocation: () => {pathname: string, search: string, key: string}, Link: import('react').ElementType}}\n */\nexport function createBrowserNavigation({basePath = ''} = {}) {\n // cambia de página: -1 regresa en el historial; una ruta de la app abre esa ruta con el prefijo\n const navigate = (destination) => {\n if (destination === HISTORY_BACK) {\n window.history.back();\n return;\n }\n
|
|
1
|
+
{"version":3,"file":"browserNavigation.js","sources":["../../src/toolkit/browserNavigation.js"],"sourcesContent":["// La navegación por omisión: la del navegador, para usar el paquete sin router.\n//\n// Los componentes que navegan (BackButton, MetricCard, el regreso a la última lista) no importan react-router ni\n// Next.js: le piden al proveedor una navegación con tres piezas, las mismas que trae react-router:\n// useNavigate() regresa navigate(destino), donde destino es una ruta o -1 para regresar;\n// useLocation() regresa {pathname, search, key} de la página actual;\n// Link el enlace, que recibe `to` con la ruta.\n// Un proyecto con router pasa su adaptador (@stackcraftmx/calcita/react-router); sin él, esta cambia de página como un\n// enlace normal.\n//\n// Para un sistema que ya existe, sin router:\n// - con prefijo (la app detrás de un proxy en /portal): createBrowserNavigation({basePath: '/portal'}). Los componentes\n// siguen hablando de '/pedidos' y la dirección real lleva el prefijo;\n// - con # en la dirección (un servidor que no sabe responder /pedidos al recargar): createHashNavigation(), en\n// hashNavigation.js.\n//\n// OJO: aquí cada navegación recarga la página. Para una aplicación de una sola página se usa el adaptador de su router (o la\n// de #, que no recarga).\nimport {createElement, useSyncExternalStore} from 'react';\n\nimport {isPlainLeftClick, pathWithBase, pathWithoutBase} from '@/toolkit/navigationPaths.js';\n\nconst HISTORY_BACK = -1;\n\n/**\n * Avisa cuando cambia la página por el historial (atrás y adelante).\n *\n * @param {Function} onChange\n * @returns {Function} para dejar de escuchar\n */\nfunction subscribeToHistory(onChange) {\n window.addEventListener('popstate', onChange);\n\n return () => window.removeEventListener('popstate', onChange);\n}\n\n/**\n * La dirección actual como texto, para saber si cambió.\n *\n * @returns {string}\n */\nfunction currentHref() {\n return window.location.href;\n}\n\n/**\n * La navegación del navegador, con o sin prefijo.\n *\n * @param {{basePath?: string}} [options] basePath: donde vive la app detrás de un proxy ('/portal'); sin él, la raíz\n * @returns {{useNavigate: () => (destination: string|number, options?: {replace?: boolean}) => void, useLocation: () => {pathname: string, search: string, key: string}, Link: import('react').ElementType}}\n */\nexport function createBrowserNavigation({basePath = ''} = {}) {\n // cambia de página: -1 regresa en el historial; una ruta de la app abre esa ruta con el prefijo. Con {replace: true} la\n // página nueva toma el lugar de la actual en el historial (como react-router): «atrás» ya no pasa por ella\n const navigate = (destination, options) => {\n if (destination === HISTORY_BACK) {\n window.history.back();\n return;\n }\n const href = pathWithBase(basePath, destination);\n if (options?.replace) {\n window.location.replace(href);\n return;\n }\n window.location.assign(href);\n };\n\n // la página actual como la da react-router (la ruta SIN el prefijo, la búsqueda y una llave por página)\n const useBrowserLocation = () => {\n const href = useSyncExternalStore(subscribeToHistory, currentHref, () => '');\n const url = href ? new URL(href) : null;\n return {pathname: url ? pathWithoutBase(basePath, url.pathname) : '/', search: url?.search ?? '', key: href};\n };\n\n // el enlace sin router: un <a> con la dirección real. Con `replace`, el clic normal reemplaza la página en el historial;\n // Cmd/Ctrl+clic y el botón de en medio siguen abriendo otra pestaña\n const BrowserLink = ({to, replace, onClick, ...props}) => {\n const href = pathWithBase(basePath, to);\n const handleClick = (event) => {\n onClick?.(event);\n if (replace && isPlainLeftClick(event, props.target)) {\n event.preventDefault();\n window.location.replace(href);\n }\n };\n return createElement('a', {href, ...props, onClick: handleClick});\n };\n\n return Object.freeze({useNavigate: () => navigate, useLocation: useBrowserLocation, Link: BrowserLink});\n}\n\n/** La de omisión: sin prefijo. */\nexport const browserNavigation = createBrowserNavigation();\n"],"names":[],"mappings":";;;AAsBA,MAAM,eAAe;AAQrB,SAAS,mBAAmB,UAAU;AAClC,SAAO,iBAAiB,YAAY,QAAQ;AAE5C,SAAO,MAAM,OAAO,oBAAoB,YAAY,QAAQ;AAChE;AAOA,SAAS,cAAc;AACnB,SAAO,OAAO,SAAS;AAC3B;AAQO,SAAS,wBAAwB,EAAC,WAAW,GAAE,IAAI,CAAA,GAAI;AAG1D,QAAM,WAAW,CAAC,aAAa,YAAY;AACvC,QAAI,gBAAgB,cAAc;AAC9B,aAAO,QAAQ,KAAI;AACnB;AAAA,IACJ;AACA,UAAM,OAAO,aAAa,UAAU,WAAW;AAC/C,QAAI,SAAS,SAAS;AAClB,aAAO,SAAS,QAAQ,IAAI;AAC5B;AAAA,IACJ;AACA,WAAO,SAAS,OAAO,IAAI;AAAA,EAC/B;AAGA,QAAM,qBAAqB,MAAM;AAC7B,UAAM,OAAO,qBAAqB,oBAAoB,aAAa,MAAM,EAAE;AAC3E,UAAM,MAAM,OAAO,IAAI,IAAI,IAAI,IAAI;AACnC,WAAO,EAAC,UAAU,MAAM,gBAAgB,UAAU,IAAI,QAAQ,IAAI,KAAK,QAAQ,KAAK,UAAU,IAAI,KAAK,KAAI;AAAA,EAC/G;AAIA,QAAM,cAAc,CAAC,EAAC,IAAI,SAAS,SAAS,GAAG,MAAK,MAAM;AACtD,UAAM,OAAO,aAAa,UAAU,EAAE;AACtC,UAAM,cAAc,CAAC,UAAU;AAC3B,gBAAU,KAAK;AACf,UAAI,WAAW,iBAAiB,OAAO,MAAM,MAAM,GAAG;AAClD,cAAM,eAAc;AACpB,eAAO,SAAS,QAAQ,IAAI;AAAA,MAChC;AAAA,IACJ;AACA,WAAO,cAAc,KAAK,EAAC,MAAM,GAAG,OAAO,SAAS,YAAW,CAAC;AAAA,EACpE;AAEA,SAAO,OAAO,OAAO,EAAC,aAAa,MAAM,UAAU,aAAa,oBAAoB,MAAM,YAAW,CAAC;AAC1G;AAGY,MAAC,oBAAoB,wBAAuB;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function mergeKeys(current, added, place) {
|
|
2
|
+
const merged = { ...current };
|
|
3
|
+
for (const [key, value] of Object.entries(added)) {
|
|
4
|
+
const existing = merged[key];
|
|
5
|
+
const bothObjects = existing && typeof existing === "object" && value && typeof value === "object";
|
|
6
|
+
if (bothObjects) {
|
|
7
|
+
merged[key] = mergeKeys(existing, value, { ...place, path: [...place.path, key] });
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (Object.hasOwn(merged, key)) {
|
|
11
|
+
throw new Error(`Dos módulos definen el texto «${place.namespace}:${[...place.path, key].join(".")}» en «${place.language}»`);
|
|
12
|
+
}
|
|
13
|
+
merged[key] = value;
|
|
14
|
+
}
|
|
15
|
+
return merged;
|
|
16
|
+
}
|
|
17
|
+
function combineTexts(textFiles) {
|
|
18
|
+
const combined = {};
|
|
19
|
+
for (const moduleTexts of textFiles) {
|
|
20
|
+
for (const [language, namespaces] of Object.entries(moduleTexts ?? {})) {
|
|
21
|
+
combined[language] ??= {};
|
|
22
|
+
for (const [namespace, keys] of Object.entries(namespaces)) {
|
|
23
|
+
combined[language][namespace] = mergeKeys(combined[language][namespace] ?? {}, keys, { language, namespace, path: [] });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return combined;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
combineTexts
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=combineTexts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combineTexts.js","sources":["../../src/toolkit/combineTexts.js"],"sourcesContent":["// Junta los textos de cada módulo en el objeto `texts` que recibe ToolkitProvider: {es: {namespace: {...}}, en: {...}}.\n//\n// Cada módulo trae su archivo de textos con sus namespaces (orders, customers...) y, si quiere, entradas de un namespace\n// compartido (dictionaries, validation). Aquí se juntan por idioma y por namespace: dos módulos con el mismo namespace suman\n// sus llaves, y si repiten una llave TRUENA: un texto pisado en silencio sale en la pantalla equivocada y nadie sabe de dónde\n// vino. Los objetos de adentro también se suman (validation.fields de dos módulos), con la misma regla.\n//\n// // src/app/appTexts.js — los archivos de textos se descubren solos (el glob tiene que ser un texto literal)\n// const files = import.meta.glob('@/modules/**/*Texts.js', {eager: true});\n// export const APP_TEXTS = combineTexts(Object.values(files).flatMap((file) => Object.values(file)));\n//\n// Funciones puras, sin React: function (regla de la casa). Pruebas en combineTexts.test.js.\n\n/**\n * Suma las llaves de un namespace: los objetos se juntan por dentro; una llave con texto repetida truena.\n *\n * @param {object} current lo que ya hay\n * @param {object} added lo que trae otro módulo\n * @param {{language: string, namespace: string, path: string[]}} place dónde va, para decir dónde chocaron\n * @returns {object}\n */\nfunction mergeKeys(current, added, place) {\n const merged = {...current};\n for (const [key, value] of Object.entries(added)) {\n const existing = merged[key];\n const bothObjects = existing && typeof existing === 'object' && value && typeof value === 'object';\n if (bothObjects) {\n merged[key] = mergeKeys(existing, value, {...place, path: [...place.path, key]});\n continue;\n }\n if (Object.hasOwn(merged, key)) {\n throw new Error(`Dos módulos definen el texto «${place.namespace}:${[...place.path, key].join('.')}» en «${place.language}»`);\n }\n merged[key] = value;\n }\n return merged;\n}\n\n/**\n * @param {Array<Record<string, Record<string, object>>>} textFiles los textos de cada módulo, por idioma\n * @returns {Record<string, Record<string, object>>} todos los textos, por idioma y namespace\n * @throws {Error} si dos módulos definen el mismo texto\n */\nexport function combineTexts(textFiles) {\n const combined = {};\n for (const moduleTexts of textFiles) {\n for (const [language, namespaces] of Object.entries(moduleTexts ?? {})) {\n combined[language] ??= {};\n for (const [namespace, keys] of Object.entries(namespaces)) {\n combined[language][namespace] = mergeKeys(combined[language][namespace] ?? {}, keys, {language, namespace, path: []});\n }\n }\n }\n return combined;\n}\n"],"names":[],"mappings":"AAqBA,SAAS,UAAU,SAAS,OAAO,OAAO;AACtC,QAAM,SAAS,EAAC,GAAG,QAAO;AAC1B,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC9C,UAAM,WAAW,OAAO,GAAG;AAC3B,UAAM,cAAc,YAAY,OAAO,aAAa,YAAY,SAAS,OAAO,UAAU;AAC1F,QAAI,aAAa;AACb,aAAO,GAAG,IAAI,UAAU,UAAU,OAAO,EAAC,GAAG,OAAO,MAAM,CAAC,GAAG,MAAM,MAAM,GAAG,EAAC,CAAC;AAC/E;AAAA,IACJ;AACA,QAAI,OAAO,OAAO,QAAQ,GAAG,GAAG;AAC5B,YAAM,IAAI,MAAM,iCAAiC,MAAM,SAAS,IAAI,CAAC,GAAG,MAAM,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC,SAAS,MAAM,QAAQ,GAAG;AAAA,IAChI;AACA,WAAO,GAAG,IAAI;AAAA,EAClB;AACA,SAAO;AACX;AAOO,SAAS,aAAa,WAAW;AACpC,QAAM,WAAW,CAAA;AACjB,aAAW,eAAe,WAAW;AACjC,eAAW,CAAC,UAAU,UAAU,KAAK,OAAO,QAAQ,eAAe,CAAA,CAAE,GAAG;AACpE,eAAS,QAAQ,MAAM,CAAA;AACvB,iBAAW,CAAC,WAAW,IAAI,KAAK,OAAO,QAAQ,UAAU,GAAG;AACxD,iBAAS,QAAQ,EAAE,SAAS,IAAI,UAAU,SAAS,QAAQ,EAAE,SAAS,KAAK,CAAA,GAAI,MAAM,EAAC,UAAU,WAAW,MAAM,CAAA,EAAE,CAAC;AAAA,MACxH;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createElement, useSyncExternalStore } from "react";
|
|
3
|
-
import { hashHrefOf, hashLocationOf } from "./navigationPaths.js";
|
|
3
|
+
import { hashHrefOf, hashLocationOf, isPlainLeftClick } from "./navigationPaths.js";
|
|
4
4
|
const HISTORY_BACK = -1;
|
|
5
5
|
function subscribeToHash(onChange) {
|
|
6
6
|
window.addEventListener("hashchange", onChange);
|
|
@@ -14,13 +14,17 @@ function currentHref() {
|
|
|
14
14
|
return window.location.href;
|
|
15
15
|
}
|
|
16
16
|
function createHashNavigation() {
|
|
17
|
-
const navigate = (destination) => {
|
|
17
|
+
const navigate = (destination, options) => {
|
|
18
18
|
if (destination === HISTORY_BACK) {
|
|
19
19
|
window.history.back();
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const href = hashHrefOf(destination);
|
|
23
23
|
if (href.startsWith("#")) {
|
|
24
|
+
if (options?.replace) {
|
|
25
|
+
window.location.replace(href);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
24
28
|
window.location.hash = href;
|
|
25
29
|
return;
|
|
26
30
|
}
|
|
@@ -31,7 +35,17 @@ function createHashNavigation() {
|
|
|
31
35
|
const { pathname, search } = hashLocationOf(href ? new URL(href).hash : "");
|
|
32
36
|
return { pathname, search, key: href };
|
|
33
37
|
};
|
|
34
|
-
const HashLink = ({ to, ...props }) =>
|
|
38
|
+
const HashLink = ({ to, replace, onClick, ...props }) => {
|
|
39
|
+
const href = hashHrefOf(to);
|
|
40
|
+
const handleClick = (event) => {
|
|
41
|
+
onClick?.(event);
|
|
42
|
+
if (replace && isPlainLeftClick(event, props.target)) {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
window.location.replace(href);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
return createElement("a", { href, ...props, onClick: handleClick });
|
|
48
|
+
};
|
|
35
49
|
return Object.freeze({ useNavigate: () => navigate, useLocation: useHashLocation, Link: HashLink });
|
|
36
50
|
}
|
|
37
51
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hashNavigation.js","sources":["../../src/toolkit/hashNavigation.js"],"sourcesContent":["// La navegación con # en la dirección (/app.php#/pedidos), sin router: para meter Calcita en un sistema que ya existe y cuyo\n// servidor no sabe responder /pedidos al recargar. Todo lo que va después del # se queda en el navegador: cambiar de\n// pantalla no recarga la página ni pide nada al servidor, y atrás y adelante funcionan.\n//\n// <ToolkitProvider navigation={createHashNavigation()}> // una vez, fuera del componente\n//\n// Con react-router se usa su createHashRouter con el adaptador de siempre (@stackcraftmx/calcita/react-router).\n//\n// Funciones genéricas: function (regla de la casa).\nimport {createElement, useSyncExternalStore} from 'react';\n\nimport {hashHrefOf, hashLocationOf} from '@/toolkit/navigationPaths.js';\n\nconst HISTORY_BACK = -1;\n\n/** Avisa cuando cambia lo que va después del # (un enlace, atrás o adelante). */\nfunction subscribeToHash(onChange) {\n window.addEventListener('hashchange', onChange);\n window.addEventListener('popstate', onChange);\n return () => {\n window.removeEventListener('hashchange', onChange);\n window.removeEventListener('popstate', onChange);\n };\n}\n\nfunction currentHref() {\n return window.location.href;\n}\n\n/**\n * @returns {{useNavigate: () => (destination: string|number) => void, useLocation: () => {pathname: string, search: string, key: string}, Link: import('react').ElementType}}\n */\nexport function createHashNavigation() {\n // -1 regresa en el historial; una ruta de la app cambia lo que va después del # (sin recargar)\n const navigate = (destination) => {\n if (destination === HISTORY_BACK) {\n window.history.back();\n return;\n }\n const href = hashHrefOf(destination);\n if (href.startsWith('#')) {\n window.location.hash = href;\n return;\n }\n // una dirección de afuera: como un enlace normal\n window.location.assign(href);\n };\n\n // la página de la app, leída de lo que va después del #\n const useHashLocation = () => {\n const href = useSyncExternalStore(subscribeToHash, currentHref, () => '');\n const {pathname, search} = hashLocationOf(href ? new URL(href).hash : '');\n return {pathname, search, key: href};\n };\n\n const HashLink = ({to, ...props}) => createElement('a', {href
|
|
1
|
+
{"version":3,"file":"hashNavigation.js","sources":["../../src/toolkit/hashNavigation.js"],"sourcesContent":["// La navegación con # en la dirección (/app.php#/pedidos), sin router: para meter Calcita en un sistema que ya existe y cuyo\n// servidor no sabe responder /pedidos al recargar. Todo lo que va después del # se queda en el navegador: cambiar de\n// pantalla no recarga la página ni pide nada al servidor, y atrás y adelante funcionan.\n//\n// <ToolkitProvider navigation={createHashNavigation()}> // una vez, fuera del componente\n//\n// Con react-router se usa su createHashRouter con el adaptador de siempre (@stackcraftmx/calcita/react-router).\n//\n// Funciones genéricas: function (regla de la casa).\nimport {createElement, useSyncExternalStore} from 'react';\n\nimport {hashHrefOf, hashLocationOf, isPlainLeftClick} from '@/toolkit/navigationPaths.js';\n\nconst HISTORY_BACK = -1;\n\n/** Avisa cuando cambia lo que va después del # (un enlace, atrás o adelante). */\nfunction subscribeToHash(onChange) {\n window.addEventListener('hashchange', onChange);\n window.addEventListener('popstate', onChange);\n return () => {\n window.removeEventListener('hashchange', onChange);\n window.removeEventListener('popstate', onChange);\n };\n}\n\nfunction currentHref() {\n return window.location.href;\n}\n\n/**\n * @returns {{useNavigate: () => (destination: string|number, options?: {replace?: boolean}) => void, useLocation: () => {pathname: string, search: string, key: string}, Link: import('react').ElementType}}\n */\nexport function createHashNavigation() {\n // -1 regresa en el historial; una ruta de la app cambia lo que va después del # (sin recargar). Con {replace: true}\n // toma el lugar de la actual en el historial\n const navigate = (destination, options) => {\n if (destination === HISTORY_BACK) {\n window.history.back();\n return;\n }\n const href = hashHrefOf(destination);\n if (href.startsWith('#')) {\n if (options?.replace) {\n window.location.replace(href);\n return;\n }\n window.location.hash = href;\n return;\n }\n // una dirección de afuera: como un enlace normal\n window.location.assign(href);\n };\n\n // la página de la app, leída de lo que va después del #\n const useHashLocation = () => {\n const href = useSyncExternalStore(subscribeToHash, currentHref, () => '');\n const {pathname, search} = hashLocationOf(href ? new URL(href).hash : '');\n return {pathname, search, key: href};\n };\n\n // con `replace`, el clic normal reemplaza la entrada del historial; Cmd/Ctrl+clic abre otra pestaña como siempre\n const HashLink = ({to, replace, onClick, ...props}) => {\n const href = hashHrefOf(to);\n const handleClick = (event) => {\n onClick?.(event);\n if (replace && isPlainLeftClick(event, props.target)) {\n event.preventDefault();\n window.location.replace(href);\n }\n };\n return createElement('a', {href, ...props, onClick: handleClick});\n };\n\n return Object.freeze({useNavigate: () => navigate, useLocation: useHashLocation, Link: HashLink});\n}\n"],"names":[],"mappings":";;;AAaA,MAAM,eAAe;AAGrB,SAAS,gBAAgB,UAAU;AAC/B,SAAO,iBAAiB,cAAc,QAAQ;AAC9C,SAAO,iBAAiB,YAAY,QAAQ;AAC5C,SAAO,MAAM;AACT,WAAO,oBAAoB,cAAc,QAAQ;AACjD,WAAO,oBAAoB,YAAY,QAAQ;AAAA,EACnD;AACJ;AAEA,SAAS,cAAc;AACnB,SAAO,OAAO,SAAS;AAC3B;AAKO,SAAS,uBAAuB;AAGnC,QAAM,WAAW,CAAC,aAAa,YAAY;AACvC,QAAI,gBAAgB,cAAc;AAC9B,aAAO,QAAQ,KAAI;AACnB;AAAA,IACJ;AACA,UAAM,OAAO,WAAW,WAAW;AACnC,QAAI,KAAK,WAAW,GAAG,GAAG;AACtB,UAAI,SAAS,SAAS;AAClB,eAAO,SAAS,QAAQ,IAAI;AAC5B;AAAA,MACJ;AACA,aAAO,SAAS,OAAO;AACvB;AAAA,IACJ;AAEA,WAAO,SAAS,OAAO,IAAI;AAAA,EAC/B;AAGA,QAAM,kBAAkB,MAAM;AAC1B,UAAM,OAAO,qBAAqB,iBAAiB,aAAa,MAAM,EAAE;AACxE,UAAM,EAAC,UAAU,OAAM,IAAI,eAAe,OAAO,IAAI,IAAI,IAAI,EAAE,OAAO,EAAE;AACxE,WAAO,EAAC,UAAU,QAAQ,KAAK,KAAI;AAAA,EACvC;AAGA,QAAM,WAAW,CAAC,EAAC,IAAI,SAAS,SAAS,GAAG,MAAK,MAAM;AACnD,UAAM,OAAO,WAAW,EAAE;AAC1B,UAAM,cAAc,CAAC,UAAU;AAC3B,gBAAU,KAAK;AACf,UAAI,WAAW,iBAAiB,OAAO,MAAM,MAAM,GAAG;AAClD,cAAM,eAAc;AACpB,eAAO,SAAS,QAAQ,IAAI;AAAA,MAChC;AAAA,IACJ;AACA,WAAO,cAAc,KAAK,EAAC,MAAM,GAAG,OAAO,SAAS,YAAW,CAAC;AAAA,EACpE;AAEA,SAAO,OAAO,OAAO,EAAC,aAAa,MAAM,UAAU,aAAa,iBAAiB,MAAM,SAAQ,CAAC;AACpG;"}
|
|
@@ -39,9 +39,24 @@ function hashLocationOf(hash) {
|
|
|
39
39
|
function hashHrefOf(destination) {
|
|
40
40
|
return isAppPath(destination) ? `${HASH_SIGN}${destination}` : destination;
|
|
41
41
|
}
|
|
42
|
+
const MAIN_MOUSE_BUTTON = 0;
|
|
43
|
+
function isPlainLeftClick(event, target) {
|
|
44
|
+
if (target && target !== "_self") {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return event.button === MAIN_MOUSE_BUTTON && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.defaultPrevented;
|
|
48
|
+
}
|
|
49
|
+
function documentBasePath(currentDocument = globalThis.document) {
|
|
50
|
+
if (!currentDocument?.querySelector?.("base[href]") || !currentDocument.baseURI) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return normalizedBasePath(new URL(currentDocument.baseURI).pathname);
|
|
54
|
+
}
|
|
42
55
|
export {
|
|
56
|
+
documentBasePath,
|
|
43
57
|
hashHrefOf,
|
|
44
58
|
hashLocationOf,
|
|
59
|
+
isPlainLeftClick,
|
|
45
60
|
normalizedBasePath,
|
|
46
61
|
pathWithBase,
|
|
47
62
|
pathWithoutBase
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigationPaths.js","sources":["../../src/toolkit/navigationPaths.js"],"sourcesContent":["// Las rutas de la navegación sin router, para sistemas que ya existen (una página de PHP, JSP o .NET con React en un pedazo):\n// - con prefijo: la app vive en /portal/… detrás de un proxy, y los componentes hablan de /pedidos sin saberlo;\n// - con # en la dirección (/app.php#/pedidos): el servidor viejo no sabe responder /pedidos al recargar, y todo lo que va\n// después del # se queda en el navegador.\n//\n// Funciones puras, no renderizan: function (regla de la casa).\n\nconst HASH_SIGN = '#';\nconst ROOT_PATH = '/';\n\n/**\n * El prefijo limpio: '' sin prefijo; si no, empieza con / y no termina con / ('portal/' → '/portal').\n *\n * @param {string|null|undefined} basePath\n * @returns {string}\n */\nexport function normalizedBasePath(basePath) {\n const trimmed = (basePath ?? '').trim().replace(/\\/+$/, '');\n if (!trimmed) {\n return '';\n }\n return trimmed.startsWith(ROOT_PATH) ? trimmed : `${ROOT_PATH}${trimmed}`;\n}\n\n/**\n * ¿Es una ruta de la app ('/pedidos')? Una dirección de afuera ('https://…', '//otro.sitio') o relativa ('?pagina=2') no.\n *\n * @param {string} destination\n * @returns {boolean}\n */\nfunction isAppPath(destination) {\n return destination.startsWith(ROOT_PATH) && !destination.startsWith('//');\n}\n\n/**\n * La dirección real de una ruta de la app con el prefijo puesto: '/pedidos' → '/portal/pedidos'. Lo que no es ruta de la\n * app se queda como viene.\n *\n * @param {string} basePath\n * @param {string} destination\n * @returns {string}\n */\nexport function pathWithBase(basePath, destination) {\n const base = normalizedBasePath(basePath);\n return base && isAppPath(destination) ? `${base}${destination}` : destination;\n}\n\n/**\n * La ruta de la app de una dirección real, sin el prefijo: '/portal/pedidos' → '/pedidos'; el prefijo solo → '/'. Una\n * dirección de fuera del prefijo se queda como viene.\n *\n * @param {string} basePath\n * @param {string} pathname\n * @returns {string}\n */\nexport function pathWithoutBase(basePath, pathname) {\n const base = normalizedBasePath(basePath);\n if (!base) {\n return pathname;\n }\n if (pathname === base) {\n return ROOT_PATH;\n }\n return pathname.startsWith(`${base}${ROOT_PATH}`) ? pathname.slice(base.length) : pathname;\n}\n\n/**\n * La página de la app que va después del # ('#/pedidos?estado=pendiente' → {pathname: '/pedidos', search: '?estado=pendiente'}).\n * Sin nada después del #, el inicio.\n *\n * @param {string} hash el de la dirección (location.hash), con o sin #\n * @returns {{pathname: string, search: string}}\n */\nexport function hashLocationOf(hash) {\n const route = (hash ?? '').replace(/^#/, '');\n if (!route) {\n return {pathname: ROOT_PATH, search: ''};\n }\n const searchStart = route.indexOf('?');\n const pathname = searchStart === -1 ? route : route.slice(0, searchStart);\n return {\n pathname: pathname.startsWith(ROOT_PATH) ? pathname : `${ROOT_PATH}${pathname}`,\n search: searchStart === -1 ? '' : route.slice(searchStart),\n };\n}\n\n/**\n * El enlace de una ruta de la app con # ('/pedidos' → '#/pedidos'). Lo que no es ruta de la app se queda como viene.\n *\n * @param {string} destination\n * @returns {string}\n */\nexport function hashHrefOf(destination) {\n return isAppPath(destination) ? `${HASH_SIGN}${destination}` : destination;\n}\n"],"names":[],"mappings":"AAOA,MAAM,YAAY;AAClB,MAAM,YAAY;AAQX,SAAS,mBAAmB,UAAU;AACzC,QAAM,WAAW,YAAY,IAAI,KAAI,EAAG,QAAQ,QAAQ,EAAE;AAC1D,MAAI,CAAC,SAAS;AACV,WAAO;AAAA,EACX;AACA,SAAO,QAAQ,WAAW,SAAS,IAAI,UAAU,GAAG,SAAS,GAAG,OAAO;AAC3E;AAQA,SAAS,UAAU,aAAa;AAC5B,SAAO,YAAY,WAAW,SAAS,KAAK,CAAC,YAAY,WAAW,IAAI;AAC5E;AAUO,SAAS,aAAa,UAAU,aAAa;AAChD,QAAM,OAAO,mBAAmB,QAAQ;AACxC,SAAO,QAAQ,UAAU,WAAW,IAAI,GAAG,IAAI,GAAG,WAAW,KAAK;AACtE;AAUO,SAAS,gBAAgB,UAAU,UAAU;AAChD,QAAM,OAAO,mBAAmB,QAAQ;AACxC,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,EACX;AACA,MAAI,aAAa,MAAM;AACnB,WAAO;AAAA,EACX;AACA,SAAO,SAAS,WAAW,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,SAAS,MAAM,KAAK,MAAM,IAAI;AACtF;AASO,SAAS,eAAe,MAAM;AACjC,QAAM,SAAS,QAAQ,IAAI,QAAQ,MAAM,EAAE;AAC3C,MAAI,CAAC,OAAO;AACR,WAAO,EAAC,UAAU,WAAW,QAAQ,GAAE;AAAA,EAC3C;AACA,QAAM,cAAc,MAAM,QAAQ,GAAG;AACrC,QAAM,WAAW,gBAAgB,KAAK,QAAQ,MAAM,MAAM,GAAG,WAAW;AACxE,SAAO;AAAA,IACH,UAAU,SAAS,WAAW,SAAS,IAAI,WAAW,GAAG,SAAS,GAAG,QAAQ;AAAA,IAC7E,QAAQ,gBAAgB,KAAK,KAAK,MAAM,MAAM,WAAW;AAAA,EACjE;AACA;AAQO,SAAS,WAAW,aAAa;AACpC,SAAO,UAAU,WAAW,IAAI,GAAG,SAAS,GAAG,WAAW,KAAK;AACnE;"}
|
|
1
|
+
{"version":3,"file":"navigationPaths.js","sources":["../../src/toolkit/navigationPaths.js"],"sourcesContent":["// Las rutas de la navegación sin router, para sistemas que ya existen (una página de PHP, JSP o .NET con React en un pedazo):\n// - con prefijo: la app vive en /portal/… detrás de un proxy, y los componentes hablan de /pedidos sin saberlo;\n// - con # en la dirección (/app.php#/pedidos): el servidor viejo no sabe responder /pedidos al recargar, y todo lo que va\n// después del # se queda en el navegador.\n//\n// Funciones puras, no renderizan: function (regla de la casa).\n\nconst HASH_SIGN = '#';\nconst ROOT_PATH = '/';\n\n/**\n * El prefijo limpio: '' sin prefijo; si no, empieza con / y no termina con / ('portal/' → '/portal').\n *\n * @param {string|null|undefined} basePath\n * @returns {string}\n */\nexport function normalizedBasePath(basePath) {\n const trimmed = (basePath ?? '').trim().replace(/\\/+$/, '');\n if (!trimmed) {\n return '';\n }\n return trimmed.startsWith(ROOT_PATH) ? trimmed : `${ROOT_PATH}${trimmed}`;\n}\n\n/**\n * ¿Es una ruta de la app ('/pedidos')? Una dirección de afuera ('https://…', '//otro.sitio') o relativa ('?pagina=2') no.\n *\n * @param {string} destination\n * @returns {boolean}\n */\nfunction isAppPath(destination) {\n return destination.startsWith(ROOT_PATH) && !destination.startsWith('//');\n}\n\n/**\n * La dirección real de una ruta de la app con el prefijo puesto: '/pedidos' → '/portal/pedidos'. Lo que no es ruta de la\n * app se queda como viene.\n *\n * @param {string} basePath\n * @param {string} destination\n * @returns {string}\n */\nexport function pathWithBase(basePath, destination) {\n const base = normalizedBasePath(basePath);\n return base && isAppPath(destination) ? `${base}${destination}` : destination;\n}\n\n/**\n * La ruta de la app de una dirección real, sin el prefijo: '/portal/pedidos' → '/pedidos'; el prefijo solo → '/'. Una\n * dirección de fuera del prefijo se queda como viene.\n *\n * @param {string} basePath\n * @param {string} pathname\n * @returns {string}\n */\nexport function pathWithoutBase(basePath, pathname) {\n const base = normalizedBasePath(basePath);\n if (!base) {\n return pathname;\n }\n if (pathname === base) {\n return ROOT_PATH;\n }\n return pathname.startsWith(`${base}${ROOT_PATH}`) ? pathname.slice(base.length) : pathname;\n}\n\n/**\n * La página de la app que va después del # ('#/pedidos?estado=pendiente' → {pathname: '/pedidos', search: '?estado=pendiente'}).\n * Sin nada después del #, el inicio.\n *\n * @param {string} hash el de la dirección (location.hash), con o sin #\n * @returns {{pathname: string, search: string}}\n */\nexport function hashLocationOf(hash) {\n const route = (hash ?? '').replace(/^#/, '');\n if (!route) {\n return {pathname: ROOT_PATH, search: ''};\n }\n const searchStart = route.indexOf('?');\n const pathname = searchStart === -1 ? route : route.slice(0, searchStart);\n return {\n pathname: pathname.startsWith(ROOT_PATH) ? pathname : `${ROOT_PATH}${pathname}`,\n search: searchStart === -1 ? '' : route.slice(searchStart),\n };\n}\n\n/**\n * El enlace de una ruta de la app con # ('/pedidos' → '#/pedidos'). Lo que no es ruta de la app se queda como viene.\n *\n * @param {string} destination\n * @returns {string}\n */\nexport function hashHrefOf(destination) {\n return isAppPath(destination) ? `${HASH_SIGN}${destination}` : destination;\n}\n\n// el botón principal del ratón (el izquierdo)\nconst MAIN_MOUSE_BUTTON = 0;\n\n/**\n * Si un clic en un enlace es el normal: el botón principal, sin Cmd/Ctrl/Shift/Alt y sin que nadie lo haya detenido.\n * Solo ese se hace por dentro (cambiar de pantalla, reemplazar el historial); con Cmd/Ctrl+clic, Shift+clic o el botón de en\n * medio el navegador hace lo suyo (otra pestaña, otra ventana), como con cualquier liga.\n *\n * @param {{button?: number, metaKey?: boolean, ctrlKey?: boolean, shiftKey?: boolean, altKey?: boolean, defaultPrevented?: boolean}} event\n * @param {string} [target] el target del enlace: con _blank tampoco se hace por dentro\n * @returns {boolean}\n */\nexport function isPlainLeftClick(event, target) {\n if (target && target !== '_self') {\n return false;\n }\n return event.button === MAIN_MOUSE_BUTTON && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.defaultPrevented;\n}\n\n/**\n * El prefijo con que el servidor sirvió la página, leído al abrirla de su <base href> ('/ventas'), o null si la página no\n * trae <base>. Así UNA compilación sirve detrás de cualquier prefijo que decida el servidor (Laravel lo pone con\n * config('app.url'), un proxy con su ruta): el prefijo no se fija al compilar. Para el basename del router y para\n * createBrowserNavigation({basePath}).\n *\n * <base href=\"{{ rtrim(config('app.url'), '/') . '/' }}\"> (en la vista que sirve la app)\n * createBrowserRouter(routes, {basename: documentBasePath() || '/'})\n *\n * OJO: con <base>, todo lo relativo de la página (imágenes, ligas «#sección») se lee desde ese prefijo.\n *\n * @param {{baseURI?: string, querySelector?: (selector: string) => unknown}} [currentDocument] por omisión el de la página\n * @returns {string|null} '' en la raíz; null sin <base> (o sin navegador)\n */\nexport function documentBasePath(currentDocument = globalThis.document) {\n if (!currentDocument?.querySelector?.('base[href]') || !currentDocument.baseURI) {\n return null;\n }\n return normalizedBasePath(new URL(currentDocument.baseURI).pathname);\n}\n"],"names":[],"mappings":"AAOA,MAAM,YAAY;AAClB,MAAM,YAAY;AAQX,SAAS,mBAAmB,UAAU;AACzC,QAAM,WAAW,YAAY,IAAI,KAAI,EAAG,QAAQ,QAAQ,EAAE;AAC1D,MAAI,CAAC,SAAS;AACV,WAAO;AAAA,EACX;AACA,SAAO,QAAQ,WAAW,SAAS,IAAI,UAAU,GAAG,SAAS,GAAG,OAAO;AAC3E;AAQA,SAAS,UAAU,aAAa;AAC5B,SAAO,YAAY,WAAW,SAAS,KAAK,CAAC,YAAY,WAAW,IAAI;AAC5E;AAUO,SAAS,aAAa,UAAU,aAAa;AAChD,QAAM,OAAO,mBAAmB,QAAQ;AACxC,SAAO,QAAQ,UAAU,WAAW,IAAI,GAAG,IAAI,GAAG,WAAW,KAAK;AACtE;AAUO,SAAS,gBAAgB,UAAU,UAAU;AAChD,QAAM,OAAO,mBAAmB,QAAQ;AACxC,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,EACX;AACA,MAAI,aAAa,MAAM;AACnB,WAAO;AAAA,EACX;AACA,SAAO,SAAS,WAAW,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,SAAS,MAAM,KAAK,MAAM,IAAI;AACtF;AASO,SAAS,eAAe,MAAM;AACjC,QAAM,SAAS,QAAQ,IAAI,QAAQ,MAAM,EAAE;AAC3C,MAAI,CAAC,OAAO;AACR,WAAO,EAAC,UAAU,WAAW,QAAQ,GAAE;AAAA,EAC3C;AACA,QAAM,cAAc,MAAM,QAAQ,GAAG;AACrC,QAAM,WAAW,gBAAgB,KAAK,QAAQ,MAAM,MAAM,GAAG,WAAW;AACxE,SAAO;AAAA,IACH,UAAU,SAAS,WAAW,SAAS,IAAI,WAAW,GAAG,SAAS,GAAG,QAAQ;AAAA,IAC7E,QAAQ,gBAAgB,KAAK,KAAK,MAAM,MAAM,WAAW;AAAA,EACjE;AACA;AAQO,SAAS,WAAW,aAAa;AACpC,SAAO,UAAU,WAAW,IAAI,GAAG,SAAS,GAAG,WAAW,KAAK;AACnE;AAGA,MAAM,oBAAoB;AAWnB,SAAS,iBAAiB,OAAO,QAAQ;AAC5C,MAAI,UAAU,WAAW,SAAS;AAC9B,WAAO;AAAA,EACX;AACA,SAAO,MAAM,WAAW,qBAAqB,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,CAAC,MAAM,YAAY,CAAC,MAAM,UAAU,CAAC,MAAM;AAChI;AAgBO,SAAS,iBAAiB,kBAAkB,WAAW,UAAU;AACpE,MAAI,CAAC,iBAAiB,gBAAgB,YAAY,KAAK,CAAC,gBAAgB,SAAS;AAC7E,WAAO;AAAA,EACX;AACA,SAAO,mBAAmB,IAAI,IAAI,gBAAgB,OAAO,EAAE,QAAQ;AACvE;"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const SEPARATOR = ":";
|
|
2
|
+
const DEFAULT_MODULE_PATTERN = /(?:^|\/)modules\/(.+)\/[^/]+$/;
|
|
3
|
+
function fillPath(route, params = {}, name = route) {
|
|
4
|
+
const used = /* @__PURE__ */ new Set();
|
|
5
|
+
const filled = route.replace(/:([A-Za-z0-9_]+)\??/g, (whole, part) => {
|
|
6
|
+
const value = params[part];
|
|
7
|
+
if (value === void 0 || value === null || value === "") {
|
|
8
|
+
if (whole.endsWith("?")) {
|
|
9
|
+
used.add(part);
|
|
10
|
+
return "";
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`La dirección «${name}» necesita ${part}. Se le dio: ${Object.keys(params).join(", ") || "nada"}`);
|
|
13
|
+
}
|
|
14
|
+
used.add(part);
|
|
15
|
+
return encodeURIComponent(String(value));
|
|
16
|
+
}).replace(/([^:])\/{2,}/g, "$1/").replace(/(.)\/+$/, "$1");
|
|
17
|
+
const query = new URLSearchParams(
|
|
18
|
+
Object.entries(params).filter(([key, value]) => !used.has(key) && value !== void 0 && value !== null && value !== "").map(([key, value]) => [key, String(value)])
|
|
19
|
+
);
|
|
20
|
+
return query.size > 0 ? `${filled}?${query}` : filled;
|
|
21
|
+
}
|
|
22
|
+
function placeInTree(tree, steps, value) {
|
|
23
|
+
let current = tree;
|
|
24
|
+
for (const step of steps.slice(0, -1)) {
|
|
25
|
+
current[step] = current[step] ?? {};
|
|
26
|
+
current = current[step];
|
|
27
|
+
}
|
|
28
|
+
const last = steps[steps.length - 1];
|
|
29
|
+
current[last] = { ...current[last] ?? {}, ...value };
|
|
30
|
+
}
|
|
31
|
+
function createPathIndex(modules, { exportName = "paths", modulePattern = DEFAULT_MODULE_PATTERN } = {}) {
|
|
32
|
+
const tree = {};
|
|
33
|
+
for (const [file, moduleExports] of Object.entries(modules)) {
|
|
34
|
+
const published = moduleExports?.[exportName];
|
|
35
|
+
if (!published) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const match = modulePattern.exec(file);
|
|
39
|
+
if (!match) {
|
|
40
|
+
throw new Error(`No sé de qué módulo es «${file}»: el archivo va dentro de modules/<módulo>/`);
|
|
41
|
+
}
|
|
42
|
+
placeInTree(tree, match[1].split("/"), published);
|
|
43
|
+
}
|
|
44
|
+
const pathOf = (name, params = {}) => {
|
|
45
|
+
let current = tree;
|
|
46
|
+
const walked = [];
|
|
47
|
+
for (const step of name.split(SEPARATOR)) {
|
|
48
|
+
if (current === null || typeof current !== "object" || current[step] === void 0) {
|
|
49
|
+
const options = current && typeof current === "object" ? Object.keys(current).join(", ") : "(nada)";
|
|
50
|
+
throw new Error(`No hay una dirección llamada «${name}». En ${walked.join(SEPARATOR) || "el índice"} hay: ${options}`);
|
|
51
|
+
}
|
|
52
|
+
current = current[step];
|
|
53
|
+
walked.push(step);
|
|
54
|
+
}
|
|
55
|
+
if (typeof current !== "string") {
|
|
56
|
+
throw new Error(`«${name}» no es una dirección sino un grupo. Dentro hay: ${Object.keys(current).join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
return fillPath(current, params, name);
|
|
59
|
+
};
|
|
60
|
+
return Object.freeze({ pathOf, allPaths: () => tree });
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
createPathIndex,
|
|
64
|
+
fillPath
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=pathIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathIndex.js","sources":["../../src/toolkit/pathIndex.js"],"sourcesContent":["// El índice de direcciones con nombre: cada módulo publica las suyas en su archivo (paths.js de las pantallas, uris.js de\n// las del API) y el índice las junta, buscables por camino con dos puntos: pathOf('admin:clients:providerList').\n//\n// Por qué: cuando una pantalla tiene que llevar a la de OTRO módulo (el detalle de un cliente regresa a la lista de donde\n// salió), sin el índice un módulo importaría al otro y dejarían de ser independientes (se borra uno y el otro no compila).\n// No es una lista central que todos editan: el dueño de la pantalla es el dueño de su dirección y el índice se arma solo.\n//\n// // src/app/appPaths.js — el glob tiene que ser un texto literal: Vite lo resuelve al compilar\n// export const {pathOf, allPaths} = createPathIndex(import.meta.glob('@/modules/**/paths.js', {eager: true}));\n//\n// pathOf('admin:profile:detail', {groupId: 2, userId: 7675}) → '/admin/user/detail/2/7675'\n// pathOf('admin:profile:detail', {groupId: 2, userId: 7675, tab: 'docs'}) → '/admin/user/detail/2/7675?tab=docs'\n//\n// Lo que la ruta pide con dos puntos (:groupId) se sustituye; lo que sobra se va como consulta. Si el nombre no existe o falta\n// una parte, TRUENA y dice qué sí hay: un `undefined` en una dirección manda a la persona a otra pantalla sin decir por qué\n// (o a un 404 raro del API a los diez minutos de buscar).\n//\n// Lo mismo sirve para las direcciones del API (uris.js con export const uris = {...}): createPathIndex(glob, {exportName: 'uris'}).\n//\n// Funciones puras, no renderizan: function (regla de la casa). Pruebas en pathIndex.test.js.\n\nconst SEPARATOR = ':';\n// el camino de un módulo sale de su carpeta: …/modules/admin/clients/paths.js → ['admin', 'clients']\nconst DEFAULT_MODULE_PATTERN = /(?:^|\\/)modules\\/(.+)\\/[^/]+$/;\n\n/**\n * Le pone sus partes a una ruta: las que pide con dos puntos y, lo que sobre, la consulta. Los nulos y los vacíos no se\n * escriben.\n *\n * @param {string} route la ruta con sus :partes ('/orders/:orderId', ':page?' es opcional)\n * @param {Record<string, unknown>} [params]\n * @param {string} [name] cómo se llama, para decirlo si algo falta\n * @returns {string}\n * @throws {Error} si la ruta pide una parte obligatoria que no viene\n */\nexport function fillPath(route, params = {}, name = route) {\n const used = new Set();\n const filled = route.replace(/:([A-Za-z0-9_]+)\\??/g, (whole, part) => {\n const value = params[part];\n if (value === undefined || value === null || value === '') {\n // la parte opcional (:page?) puede faltar; la obligatoria, no\n if (whole.endsWith('?')) {\n used.add(part);\n return '';\n }\n throw new Error(`La dirección «${name}» necesita ${part}. Se le dio: ${Object.keys(params).join(', ') || 'nada'}`);\n }\n used.add(part);\n return encodeURIComponent(String(value));\n // la parte opcional que faltó deja «//» o una diagonal al final; «https://» se respeta\n }).replace(/([^:])\\/{2,}/g, '$1/').replace(/(.)\\/+$/, '$1');\n\n // lo que sobra es la consulta: así una liga abre una pestaña (?tab=docs) o una lista ya filtrada\n const query = new URLSearchParams(\n Object.entries(params)\n .filter(([key, value]) => !used.has(key) && value !== undefined && value !== null && value !== '')\n .map(([key, value]) => [key, String(value)]),\n );\n return query.size > 0 ? `${filled}?${query}` : filled;\n}\n\n/**\n * Mete lo de un módulo en el árbol, un tramo por nivel (admin/clients → {admin: {clients: …}}): el buscador baja nivel por\n * nivel, así que el árbol va anidado igual que se busca.\n */\nfunction placeInTree(tree, steps, value) {\n let current = tree;\n for (const step of steps.slice(0, -1)) {\n current[step] = current[step] ?? {};\n current = current[step];\n }\n const last = steps[steps.length - 1];\n current[last] = {...(current[last] ?? {}), ...value};\n}\n\n/**\n * El índice de las direcciones que publica cada módulo.\n *\n * @param {Record<string, Record<string, unknown>>} modules lo que da import.meta.glob(..., {eager: true}): archivo → sus exports\n * @param {object} [options]\n * @param {string} [options.exportName] el export con las direcciones en cada archivo (por omisión 'paths'; para el API, 'uris')\n * @param {RegExp} [options.modulePattern] saca del nombre del archivo el camino del módulo (el primer grupo, con /)\n * @returns {{pathOf: (name: string, params?: Record<string, unknown>) => string, allPaths: () => object}}\n */\nexport function createPathIndex(modules, {exportName = 'paths', modulePattern = DEFAULT_MODULE_PATTERN} = {}) {\n const tree = {};\n for (const [file, moduleExports] of Object.entries(modules)) {\n const published = moduleExports?.[exportName];\n // un archivo que no publica nada no entra al árbol\n if (!published) {\n continue;\n }\n const match = modulePattern.exec(file);\n if (!match) {\n throw new Error(`No sé de qué módulo es «${file}»: el archivo va dentro de modules/<módulo>/`);\n }\n placeInTree(tree, match[1].split('/'), published);\n }\n\n const pathOf = (name, params = {}) => {\n let current = tree;\n const walked = [];\n for (const step of name.split(SEPARATOR)) {\n if (current === null || typeof current !== 'object' || current[step] === undefined) {\n const options = current && typeof current === 'object' ? Object.keys(current).join(', ') : '(nada)';\n throw new Error(`No hay una dirección llamada «${name}». En ${walked.join(SEPARATOR) || 'el índice'} hay: ${options}`);\n }\n current = current[step];\n walked.push(step);\n }\n // quedarse a media rama da un grupo, no una dirección\n if (typeof current !== 'string') {\n throw new Error(`«${name}» no es una dirección sino un grupo. Dentro hay: ${Object.keys(current).join(', ')}`);\n }\n return fillPath(current, params, name);\n };\n\n return Object.freeze({pathOf, allPaths: () => tree});\n}\n"],"names":[],"mappings":"AAqBA,MAAM,YAAY;AAElB,MAAM,yBAAyB;AAYxB,SAAS,SAAS,OAAO,SAAS,CAAA,GAAI,OAAO,OAAO;AACvD,QAAM,OAAO,oBAAI,IAAG;AACpB,QAAM,SAAS,MAAM,QAAQ,wBAAwB,CAAC,OAAO,SAAS;AAClE,UAAM,QAAQ,OAAO,IAAI;AACzB,QAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI;AAEvD,UAAI,MAAM,SAAS,GAAG,GAAG;AACrB,aAAK,IAAI,IAAI;AACb,eAAO;AAAA,MACX;AACA,YAAM,IAAI,MAAM,iBAAiB,IAAI,cAAc,IAAI,gBAAgB,OAAO,KAAK,MAAM,EAAE,KAAK,IAAI,KAAK,MAAM,EAAE;AAAA,IACrH;AACA,SAAK,IAAI,IAAI;AACb,WAAO,mBAAmB,OAAO,KAAK,CAAC;AAAA,EAE3C,CAAC,EAAE,QAAQ,iBAAiB,KAAK,EAAE,QAAQ,WAAW,IAAI;AAG1D,QAAM,QAAQ,IAAI;AAAA,IACd,OAAO,QAAQ,MAAM,EAChB,OAAO,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,IAAI,GAAG,KAAK,UAAU,UAAa,UAAU,QAAQ,UAAU,EAAE,EAChG,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;AAAA,EACvD;AACI,SAAO,MAAM,OAAO,IAAI,GAAG,MAAM,IAAI,KAAK,KAAK;AACnD;AAMA,SAAS,YAAY,MAAM,OAAO,OAAO;AACrC,MAAI,UAAU;AACd,aAAW,QAAQ,MAAM,MAAM,GAAG,EAAE,GAAG;AACnC,YAAQ,IAAI,IAAI,QAAQ,IAAI,KAAK,CAAA;AACjC,cAAU,QAAQ,IAAI;AAAA,EAC1B;AACA,QAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,UAAQ,IAAI,IAAI,EAAC,GAAI,QAAQ,IAAI,KAAK,CAAA,GAAK,GAAG,MAAK;AACvD;AAWO,SAAS,gBAAgB,SAAS,EAAC,aAAa,SAAS,gBAAgB,uBAAsB,IAAI,IAAI;AAC1G,QAAM,OAAO,CAAA;AACb,aAAW,CAAC,MAAM,aAAa,KAAK,OAAO,QAAQ,OAAO,GAAG;AACzD,UAAM,YAAY,gBAAgB,UAAU;AAE5C,QAAI,CAAC,WAAW;AACZ;AAAA,IACJ;AACA,UAAM,QAAQ,cAAc,KAAK,IAAI;AACrC,QAAI,CAAC,OAAO;AACR,YAAM,IAAI,MAAM,2BAA2B,IAAI,8CAA8C;AAAA,IACjG;AACA,gBAAY,MAAM,MAAM,CAAC,EAAE,MAAM,GAAG,GAAG,SAAS;AAAA,EACpD;AAEA,QAAM,SAAS,CAAC,MAAM,SAAS,CAAA,MAAO;AAClC,QAAI,UAAU;AACd,UAAM,SAAS,CAAA;AACf,eAAW,QAAQ,KAAK,MAAM,SAAS,GAAG;AACtC,UAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,QAAQ,IAAI,MAAM,QAAW;AAChF,cAAM,UAAU,WAAW,OAAO,YAAY,WAAW,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,IAAI;AAC3F,cAAM,IAAI,MAAM,iCAAiC,IAAI,SAAS,OAAO,KAAK,SAAS,KAAK,WAAW,SAAS,OAAO,EAAE;AAAA,MACzH;AACA,gBAAU,QAAQ,IAAI;AACtB,aAAO,KAAK,IAAI;AAAA,IACpB;AAEA,QAAI,OAAO,YAAY,UAAU;AAC7B,YAAM,IAAI,MAAM,IAAI,IAAI,oDAAoD,OAAO,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,IACjH;AACA,WAAO,SAAS,SAAS,QAAQ,IAAI;AAAA,EACzC;AAEA,SAAO,OAAO,OAAO,EAAC,QAAQ,UAAU,MAAM,KAAI,CAAC;AACvD;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createLogger } from "../observability/logger.js";
|
|
1
2
|
import es from "../locales/es.js";
|
|
2
3
|
import en from "../locales/en.js";
|
|
3
4
|
const DEFAULT_LANGUAGE = "es";
|
|
@@ -32,7 +33,9 @@ let currentSettings = Object.freeze({
|
|
|
32
33
|
// la compilación del front que corre en esta pestaña ('' en desarrollo): con ella se sabe si hay una versión nueva
|
|
33
34
|
buildId: "",
|
|
34
35
|
// los idiomas que ofrece la app (DEFAULT_LANGUAGES)
|
|
35
|
-
languages: DEFAULT_LANGUAGES
|
|
36
|
+
languages: DEFAULT_LANGUAGES,
|
|
37
|
+
// a quién avisarle de un texto que no existe (reportMissingText); sin él, un aviso en la consola
|
|
38
|
+
onMissingText: null
|
|
36
39
|
});
|
|
37
40
|
function configureToolkit(settings) {
|
|
38
41
|
currentSettings = Object.freeze({ ...currentSettings, ...settings });
|
|
@@ -94,8 +97,23 @@ function translateWith(settings, defaultNamespace, key, values) {
|
|
|
94
97
|
if (typeof values?.defaultValue === "string") {
|
|
95
98
|
return interpolate(values.defaultValue, values);
|
|
96
99
|
}
|
|
100
|
+
reportMissingText(settings, namespace, plainKey);
|
|
97
101
|
return `${namespace}${NAMESPACE_SEPARATOR}${plainKey}`;
|
|
98
102
|
}
|
|
103
|
+
const log = createLogger("texts");
|
|
104
|
+
const warnedMissingTexts = /* @__PURE__ */ new Set();
|
|
105
|
+
function reportMissingText(settings, namespace, key) {
|
|
106
|
+
if (settings.onMissingText) {
|
|
107
|
+
settings.onMissingText({ namespace, key, language: settings.language });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const missingId = `${settings.language}${NAMESPACE_SEPARATOR}${namespace}${NAMESPACE_SEPARATOR}${key}`;
|
|
111
|
+
if (warnedMissingTexts.has(missingId)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
warnedMissingTexts.add(missingId);
|
|
115
|
+
log.warn(`No hay texto para «${namespace}${NAMESPACE_SEPARATOR}${key}» en «${settings.language}»: la pantalla enseña la llave`);
|
|
116
|
+
}
|
|
99
117
|
function translateToolkit(defaultNamespace, key, values) {
|
|
100
118
|
return translateWith(currentSettings, defaultNamespace, key, values);
|
|
101
119
|
}
|