@stackcraftmx/calcita 0.47.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -12
- package/cli/calcita.js +15 -0
- package/cli/checkTexts.js +209 -0
- package/cli/packageVersions.js +6 -1
- package/cli/projectFiles.js +4 -2
- package/cli/templates/project/README.md.tpl +21 -1
- package/cli/templates/project/eslint.config.js.tpl +27 -0
- package/cli/templates/project/src/app/appTexts.js.tpl +2 -1
- package/cli/templates/project/src/core/publicPaths.js.tpl +10 -2
- package/cli/templates/project/vite.config.js.tpl +8 -1
- package/dist/DataTable/AuditTrailDialog.js +3 -2
- package/dist/DataTable/AuditTrailDialog.js.map +1 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -2
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -2
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +3 -2
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
- package/dist/DataTable/EmptyState.js +3 -2
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +3 -2
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +3 -2
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +95 -28
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/ServerTableFilterDrawer.js +8 -3
- package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -1
- package/dist/DataTable/StackedCell.js +60 -0
- package/dist/DataTable/StackedCell.js.map +1 -0
- package/dist/DataTable/TableCardsView.js +88 -0
- package/dist/DataTable/TableCardsView.js.map +1 -0
- package/dist/DataTable/columnWidths.js +30 -0
- package/dist/DataTable/columnWidths.js.map +1 -0
- package/dist/DataTable/columns.js +47 -1
- package/dist/DataTable/columns.js.map +1 -1
- package/dist/DataTable/fixedColumns.js +24 -0
- package/dist/DataTable/fixedColumns.js.map +1 -0
- package/dist/DataTable/useTableView.js +29 -0
- package/dist/DataTable/useTableView.js.map +1 -0
- package/dist/access/PermissionMatrix.js +3 -2
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/account/AccountProvider.js +1 -1
- package/dist/account/AccountSecuritySection.js +3 -2
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/DemoTotpCodeButton.js +3 -2
- package/dist/account/DemoTotpCodeButton.js.map +1 -1
- package/dist/account/ProfileScreen.js +3 -2
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +3 -2
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/appShell/AccountMenu.js +3 -2
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +3 -2
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +3 -2
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +3 -2
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActionButton.js +11 -4
- package/dist/appShell/BarActionButton.js.map +1 -1
- package/dist/appShell/BarActions.js +3 -2
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarCapsule.js +3 -2
- package/dist/appShell/BarCapsule.js.map +1 -1
- package/dist/appShell/BarSearch.js +3 -2
- package/dist/appShell/BarSearch.js.map +1 -1
- package/dist/appShell/HeaderTools.js +3 -2
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MobileTabBar.js +3 -2
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +3 -2
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +3 -2
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +3 -2
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +3 -2
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TabBarEditor.js +3 -2
- package/dist/appShell/TabBarEditor.js.map +1 -1
- package/dist/appShell/TopMenu.js +3 -2
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +3 -2
- package/dist/appShell/Wordmark.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +3 -2
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +3 -2
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +3 -2
- package/dist/calendar/EventDetails.js.map +1 -1
- package/dist/datePickers/DateInput.js +28 -22
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/datePickers/DateTimeInput.js +33 -27
- package/dist/datePickers/DateTimeInput.js.map +1 -1
- package/dist/datePickers/MonthInput.js +27 -21
- package/dist/datePickers/MonthInput.js.map +1 -1
- package/dist/datePickers/TimeInput.js +26 -20
- package/dist/datePickers/TimeInput.js.map +1 -1
- package/dist/datePickers/usePickerField.js +5 -4
- package/dist/datePickers/usePickerField.js.map +1 -1
- package/dist/downloads/DownloadManager.js +3 -2
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/feedback/ConfirmDialog.js +3 -2
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +3 -2
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +3 -2
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +3 -2
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/forms/FormProblemAlert.js +3 -2
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/SubmitButton.js +7 -2
- package/dist/forms/SubmitButton.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +3 -2
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/fieldRules.js +30 -0
- package/dist/forms/fieldRules.js.map +1 -0
- package/dist/forms/security/SecondStepForm.js +3 -2
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +3 -2
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/forms/useValidationText.js +18 -0
- package/dist/forms/useValidationText.js.map +1 -0
- package/dist/forms/validation.js +35 -31
- package/dist/forms/validation.js.map +1 -1
- package/dist/forms/validationMessages.js +64 -7
- package/dist/forms/validationMessages.js.map +1 -1
- package/dist/forms.js +3 -0
- package/dist/forms.js.map +1 -1
- package/dist/hookForm/FormProblemAlert.js +3 -2
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/hookForm/SubmitButton.js +7 -2
- package/dist/hookForm/SubmitButton.js.map +1 -1
- package/dist/inbox/InboxLayout.js +3 -2
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +3 -2
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +3 -2
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/index.js +186 -163
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +32 -25
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/CheckboxGroupInput.js +8 -5
- package/dist/inputs/CheckboxGroupInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +7 -4
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FieldFrame.js +10 -4
- package/dist/inputs/FieldFrame.js.map +1 -1
- package/dist/inputs/FormSection.js +1 -1
- package/dist/inputs/MaskedInput.js +1 -0
- package/dist/inputs/MaskedInput.js.map +1 -1
- package/dist/inputs/NumberInput.js +29 -24
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/RepeatingFields.js +8 -5
- package/dist/inputs/RepeatingFields.js.map +1 -1
- package/dist/inputs/SelectInput.js +8 -5
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +5 -3
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/TransferListInput.js +8 -5
- package/dist/inputs/TransferListInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +3 -2
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/kanban/KanbanBoard.js +3 -2
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/legal/CookieNotice.js +3 -2
- package/dist/legal/CookieNotice.js.map +1 -1
- package/dist/legal/TermsAcceptanceDialog.js +3 -2
- package/dist/legal/TermsAcceptanceDialog.js.map +1 -1
- package/dist/locales/en.js +24 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +24 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/maps/MapView.js +3 -2
- package/dist/maps/MapView.js.map +1 -1
- package/dist/mexico/CfdiSummary.js +3 -2
- package/dist/mexico/CfdiSummary.js.map +1 -1
- package/dist/mexico/EfirmaFields.js +3 -2
- package/dist/mexico/EfirmaFields.js.map +1 -1
- package/dist/mexico/mexicoIdentifiers.js +5 -0
- package/dist/mexico/mexicoIdentifiers.js.map +1 -1
- package/dist/mexico/mexicoRules.js +9 -4
- package/dist/mexico/mexicoRules.js.map +1 -1
- package/dist/mexico.js +4 -2
- package/dist/pwa/InstallAppSetting.js +3 -2
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +3 -2
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +3 -2
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/react-hook-form.js +3 -0
- package/dist/react-hook-form.js.map +1 -1
- package/dist/richText/RichTextEditor.js +7 -4
- package/dist/richText/RichTextEditor.js.map +1 -1
- package/dist/richText/RichTextView.js +3 -2
- package/dist/richText/RichTextView.js.map +1 -1
- package/dist/security/ActivityLog.js +3 -2
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +3 -2
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +3 -2
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/table.js +21 -2
- package/dist/table.js.map +1 -1
- package/dist/theme/themePieces.js +70 -4
- package/dist/theme/themePieces.js.map +1 -1
- package/dist/toolkit/ToolkitProvider.js +8 -1
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/browserNavigation.js +19 -4
- package/dist/toolkit/browserNavigation.js.map +1 -1
- package/dist/toolkit/combineTexts.js +32 -0
- package/dist/toolkit/combineTexts.js.map +1 -0
- package/dist/toolkit/hashNavigation.js +17 -3
- package/dist/toolkit/hashNavigation.js.map +1 -1
- package/dist/toolkit/navigationPaths.js +15 -0
- package/dist/toolkit/navigationPaths.js.map +1 -1
- package/dist/toolkit/pathIndex.js +66 -0
- package/dist/toolkit/pathIndex.js.map +1 -0
- package/dist/toolkit/toolkitSettings.js +19 -1
- package/dist/toolkit/toolkitSettings.js.map +1 -1
- package/dist/types/DataTable/AuditTrailDialog.d.ts +6 -17
- package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +6 -5
- package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +6 -9
- package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +6 -14
- package/dist/types/DataTable/EmptyState.d.ts +6 -9
- package/dist/types/DataTable/FilterChips.d.ts +6 -11
- package/dist/types/DataTable/GoToPage.d.ts +6 -9
- package/dist/types/DataTable/ServerTable.d.ts +16 -80
- package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +6 -13
- package/dist/types/DataTable/StackedCell.d.ts +12 -0
- package/dist/types/DataTable/TableCardsView.d.ts +27 -0
- package/dist/types/DataTable/columnWidths.d.ts +21 -0
- package/dist/types/DataTable/columns.d.ts +111 -0
- package/dist/types/DataTable/fixedColumns.d.ts +29 -0
- package/dist/types/DataTable/index.d.ts +6 -1
- package/dist/types/DataTable/useTableView.d.ts +22 -0
- package/dist/types/access/PermissionMatrix.d.ts +6 -10
- package/dist/types/account/AccountSecuritySection.d.ts +6 -7
- package/dist/types/account/DemoTotpCodeButton.d.ts +6 -5
- package/dist/types/account/ProfileScreen.d.ts +6 -18
- package/dist/types/account/SignInScreen.d.ts +6 -13
- package/dist/types/appShell/AccountMenu.d.ts +6 -11
- package/dist/types/appShell/AppLayout.d.ts +3 -40
- package/dist/types/appShell/AppearanceSettings.d.ts +6 -12
- package/dist/types/appShell/AuthLayout.d.ts +3 -11
- package/dist/types/appShell/BarActionButton.d.ts +6 -10
- package/dist/types/appShell/BarActions.d.ts +6 -15
- package/dist/types/appShell/BarCapsule.d.ts +6 -9
- package/dist/types/appShell/BarSearch.d.ts +6 -15
- package/dist/types/appShell/HeaderTools.d.ts +6 -16
- package/dist/types/appShell/MobileTabBar.d.ts +6 -13
- package/dist/types/appShell/SettingsMenu.d.ts +6 -10
- package/dist/types/appShell/SideMenu.d.ts +6 -20
- package/dist/types/appShell/SideMenuEntry.d.ts +6 -11
- package/dist/types/appShell/SubmenuActionsMenu.d.ts +6 -12
- package/dist/types/appShell/TabBarEditor.d.ts +6 -9
- package/dist/types/appShell/TopMenu.d.ts +6 -5
- package/dist/types/appShell/Wordmark.d.ts +6 -7
- package/dist/types/auth/PasswordChecklist.d.ts +6 -9
- package/dist/types/calendar/EventCalendar.d.ts +6 -23
- package/dist/types/calendar/EventDetails.d.ts +6 -10
- package/dist/types/datePickers/DateInput.d.ts +7 -22
- package/dist/types/datePickers/DateTimeInput.d.ts +7 -27
- package/dist/types/datePickers/MonthInput.d.ts +7 -21
- package/dist/types/datePickers/TimeInput.d.ts +7 -21
- package/dist/types/datePickers/usePickerField.d.ts +5 -1
- package/dist/types/downloads/DownloadManager.d.ts +6 -6
- package/dist/types/feedback/ConfirmDialog.d.ts +6 -18
- package/dist/types/feedback/ModalWaiting.d.ts +6 -9
- package/dist/types/feedback/NewBuildNotice.d.ts +6 -10
- package/dist/types/filepond/FileDropzone.d.ts +6 -29
- package/dist/types/forms/FormProblemAlert.d.ts +6 -5
- package/dist/types/forms/SubmitButton.d.ts +6 -6
- package/dist/types/forms/auth/SignInForm.d.ts +6 -11
- package/dist/types/forms/fieldRules.d.ts +44 -0
- package/dist/types/forms/index.d.ts +1 -0
- package/dist/types/forms/security/SecondStepForm.d.ts +6 -14
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +6 -21
- package/dist/types/forms/useValidationText.d.ts +12 -0
- package/dist/types/forms/validation.d.ts +117 -174
- package/dist/types/forms/validationMessages.d.ts +54 -6
- package/dist/types/forms.d.ts +1 -1
- package/dist/types/hookForm/FormProblemAlert.d.ts +6 -5
- package/dist/types/hookForm/SubmitButton.d.ts +6 -13
- package/dist/types/inbox/InboxLayout.d.ts +6 -17
- package/dist/types/inbox/MessageList.d.ts +6 -18
- package/dist/types/inbox/MessageView.d.ts +6 -14
- package/dist/types/index.d.ts +8 -1
- package/dist/types/inputs/CatalogInput.d.ts +7 -24
- package/dist/types/inputs/CheckboxGroupInput.d.ts +7 -18
- package/dist/types/inputs/ColorInput.d.ts +7 -16
- package/dist/types/inputs/FieldFrame.d.ts +6 -10
- package/dist/types/inputs/MaskedInput.d.ts +3 -0
- package/dist/types/inputs/NumberInput.d.ts +7 -20
- package/dist/types/inputs/RepeatingFields.d.ts +7 -28
- package/dist/types/inputs/SelectInput.d.ts +7 -20
- package/dist/types/inputs/TextInput.d.ts +3 -0
- package/dist/types/inputs/TransferListInput.d.ts +7 -20
- package/dist/types/inputs/WizardLayout.d.ts +6 -19
- package/dist/types/kanban/KanbanBoard.d.ts +6 -25
- package/dist/types/legal/CookieNotice.d.ts +6 -16
- package/dist/types/legal/TermsAcceptanceDialog.d.ts +6 -18
- package/dist/types/locales/en.d.ts +24 -0
- package/dist/types/locales/es.d.ts +24 -0
- package/dist/types/maps/MapView.d.ts +6 -17
- package/dist/types/mexico/CfdiSummary.d.ts +6 -11
- package/dist/types/mexico/EfirmaFields.d.ts +6 -16
- package/dist/types/mexico/mexicoIdentifiers.d.ts +7 -0
- package/dist/types/mexico/mexicoRules.d.ts +19 -3
- package/dist/types/mexico.d.ts +2 -2
- package/dist/types/pwa/InstallAppSetting.d.ts +6 -6
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +6 -9
- package/dist/types/pwa/InstallPromptBanner.d.ts +6 -11
- package/dist/types/react-hook-form.d.ts +1 -0
- package/dist/types/richText/RichTextEditor.d.ts +7 -22
- package/dist/types/richText/RichTextView.d.ts +6 -9
- package/dist/types/security/ActivityLog.d.ts +6 -9
- package/dist/types/security/RecoveryCodesDialog.d.ts +6 -11
- package/dist/types/security/SessionsCard.d.ts +6 -11
- package/dist/types/table.d.ts +3 -1
- package/dist/types/theme/themePieces.d.ts +55 -3
- package/dist/types/toolkit/ToolkitProvider.d.ts +13 -1
- package/dist/types/toolkit/browserNavigation.d.ts +7 -3
- package/dist/types/toolkit/combineTexts.d.ts +6 -0
- package/dist/types/toolkit/hashNavigation.d.ts +4 -2
- package/dist/types/toolkit/navigationPaths.d.ts +35 -0
- package/dist/types/toolkit/pathIndex.d.ts +27 -0
- package/dist/types/toolkit/toolkitSettings.d.ts +7 -4
- package/dist/types/ui/actions/ActionsMenu.d.ts +9 -11
- package/dist/types/ui/actions/CopyButton.d.ts +6 -12
- package/dist/types/ui/actions/DownloadMenu.d.ts +6 -29
- package/dist/types/ui/actions/SecretField.d.ts +6 -11
- package/dist/types/ui/actions/SelectionActions.d.ts +6 -13
- package/dist/types/ui/cards/RecordCard.d.ts +6 -31
- package/dist/types/ui/cards/RecordCardSkeleton.d.ts +6 -11
- package/dist/types/ui/countdown/Countdown.d.ts +6 -15
- package/dist/types/ui/documents/PdfViewerDialog.d.ts +6 -13
- package/dist/types/ui/filters/DateRangeFilter.d.ts +6 -10
- package/dist/types/ui/filters/FilterDrawer.d.ts +6 -17
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +6 -13
- package/dist/types/ui/filters/QuickFilters.d.ts +6 -12
- package/dist/types/ui/help/HelpButton.d.ts +6 -10
- package/dist/types/ui/help/HelpCarouselDialog.d.ts +6 -14
- package/dist/types/ui/labels/MarkedText.d.ts +10 -0
- package/dist/types/ui/labels/Tag.d.ts +6 -12
- package/dist/types/ui/labels/markedTextPieces.d.ts +22 -0
- package/dist/types/ui/layout/CollapsibleSection.d.ts +6 -12
- package/dist/types/ui/layout/ContentDialog.d.ts +6 -10
- package/dist/types/ui/layout/DefinitionList.d.ts +6 -9
- package/dist/types/ui/layout/DetailHeader.d.ts +6 -10
- package/dist/types/ui/layout/ErrorPage.d.ts +6 -17
- package/dist/types/ui/layout/FullPageLoader.d.ts +6 -6
- package/dist/types/ui/layout/GlassPanel.d.ts +6 -8
- package/dist/types/ui/layout/PageHeader.d.ts +6 -10
- package/dist/types/ui/layout/PageHeading.d.ts +6 -11
- package/dist/types/ui/layout/ProfileHeader.d.ts +6 -11
- package/dist/types/ui/layout/ScrollableRow.d.ts +6 -13
- package/dist/types/ui/layout/SectionCard.d.ts +6 -8
- package/dist/types/ui/metrics/BarList.d.ts +6 -12
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +6 -19
- package/dist/types/ui/metrics/ChartTooltip.d.ts +6 -6
- package/dist/types/ui/metrics/ColumnChart.d.ts +6 -25
- package/dist/types/ui/metrics/LineChart.d.ts +6 -20
- package/dist/types/ui/metrics/MetricCard.d.ts +6 -17
- package/dist/types/ui/metrics/TableToggle.d.ts +6 -6
- package/dist/types/ui/metrics/chartParts.d.ts +15 -30
- package/dist/types/ui/navigation/BackButton.d.ts +6 -9
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +6 -9
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +6 -8
- package/dist/types/ui/navigation/LanguageToggle.d.ts +6 -8
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +6 -7
- package/dist/types/ui/navigation/RouteTabs.d.ts +7 -12
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +6 -23
- package/dist/types/upload/AttachmentsInput.d.ts +11 -1
- package/dist/types/upload/FileDropzone.d.ts +6 -21
- package/dist/types/upload/FileInput.d.ts +7 -24
- package/dist/types/upload/FileUploadInput.d.ts +7 -31
- package/dist/types/upload/ImageInput.d.ts +7 -25
- package/dist/types/upload/TemplateDownloadButton.d.ts +6 -18
- package/dist/ui/actions/ActionsMenu.js +14 -5
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/CopyButton.js +8 -3
- package/dist/ui/actions/CopyButton.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +3 -2
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SecretField.js +8 -3
- package/dist/ui/actions/SecretField.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +3 -2
- package/dist/ui/actions/SelectionActions.js.map +1 -1
- package/dist/ui/cards/RecordCard.js +4 -3
- package/dist/ui/cards/RecordCard.js.map +1 -1
- package/dist/ui/cards/RecordCardSkeleton.js +8 -3
- package/dist/ui/cards/RecordCardSkeleton.js.map +1 -1
- package/dist/ui/countdown/Countdown.js +3 -2
- package/dist/ui/countdown/Countdown.js.map +1 -1
- package/dist/ui/documents/PdfViewerDialog.js +3 -2
- package/dist/ui/documents/PdfViewerDialog.js.map +1 -1
- package/dist/ui/filters/DateRangeFilter.js +3 -2
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/FilterDrawer.js +3 -2
- package/dist/ui/filters/FilterDrawer.js.map +1 -1
- package/dist/ui/filters/PeriodRangeSelector.js +3 -2
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +3 -2
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/help/HelpButton.js +8 -3
- package/dist/ui/help/HelpButton.js.map +1 -1
- package/dist/ui/help/HelpCarouselDialog.js +3 -2
- package/dist/ui/help/HelpCarouselDialog.js.map +1 -1
- package/dist/ui/labels/MarkedText.js +31 -0
- package/dist/ui/labels/MarkedText.js.map +1 -0
- package/dist/ui/labels/Tag.js +3 -2
- package/dist/ui/labels/Tag.js.map +1 -1
- package/dist/ui/labels/markedTextPieces.js +64 -0
- package/dist/ui/labels/markedTextPieces.js.map +1 -0
- package/dist/ui/layout/CollapsibleSection.js +3 -2
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +3 -2
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/DefinitionList.js +3 -2
- package/dist/ui/layout/DefinitionList.js.map +1 -1
- package/dist/ui/layout/DetailHeader.js +3 -2
- package/dist/ui/layout/DetailHeader.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +3 -2
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +3 -2
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/GlassPanel.js +3 -2
- package/dist/ui/layout/GlassPanel.js.map +1 -1
- package/dist/ui/layout/PageHeader.js +3 -2
- package/dist/ui/layout/PageHeader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +3 -2
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/layout/ProfileHeader.js +3 -2
- package/dist/ui/layout/ProfileHeader.js.map +1 -1
- package/dist/ui/layout/ScrollableRow.js +3 -2
- package/dist/ui/layout/ScrollableRow.js.map +1 -1
- package/dist/ui/layout/SectionCard.js +3 -2
- package/dist/ui/layout/SectionCard.js.map +1 -1
- package/dist/ui/metrics/BarList.js +3 -2
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +3 -2
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js +3 -2
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +3 -2
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +3 -2
- package/dist/ui/metrics/LineChart.js.map +1 -1
- package/dist/ui/metrics/MetricCard.js +3 -2
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +3 -2
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +7 -4
- package/dist/ui/metrics/chartParts.js.map +1 -1
- package/dist/ui/navigation/BackButton.js +7 -2
- package/dist/ui/navigation/BackButton.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +3 -2
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
- package/dist/ui/navigation/FontSizeToggle.js +3 -2
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
- package/dist/ui/navigation/LanguageToggle.js +3 -2
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +3 -2
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/navigation/RouteTabs.js +8 -4
- package/dist/ui/navigation/RouteTabs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +3 -2
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/AttachmentsInput.js +9 -6
- package/dist/upload/AttachmentsInput.js.map +1 -1
- package/dist/upload/FileChip.js +2 -1
- package/dist/upload/FileChip.js.map +1 -1
- package/dist/upload/FileDropzone.js +5 -4
- package/dist/upload/FileDropzone.js.map +1 -1
- package/dist/upload/FileInput.js +9 -6
- package/dist/upload/FileInput.js.map +1 -1
- package/dist/upload/FileUploadInput.js +9 -6
- package/dist/upload/FileUploadInput.js.map +1 -1
- package/dist/upload/ImageInput.js +8 -5
- package/dist/upload/ImageInput.js.map +1 -1
- package/dist/upload/TemplateDownloadButton.js +3 -2
- package/dist/upload/TemplateDownloadButton.js.map +1 -1
- package/package.json +1 -1
- package/cli/templates/project/src/app/combineTexts.js.tpl +0 -31
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityLog.js","sources":["../../src/security/ActivityLog.jsx"],"sourcesContent":["// La bitácora de la cuenta: lo último que pasó (entró, contraseña equivocada, cambió su contraseña, activó la verificación,\n// agregó una llave…), con la hora, el equipo y la red a medias. Para que la persona reconozca lo que no fue ella.\n//\n// Cada evento llega con su tipo (kind) y el texto sale de los textos del paquete (security:activity.kinds.<kind>); un tipo\n// propio del proyecto trae su texto en description.\n//\n// <ActivityLog events={events} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaActivityLog con defaultProps y\n// styleOverrides de sus partes: itemIcon.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Alert from '@mui/material/Alert';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';\nimport EventNoteOutlinedIcon from '@mui/icons-material/EventNoteOutlined';\nimport FingerprintOutlinedIcon from '@mui/icons-material/FingerprintOutlined';\nimport KeyOutlinedIcon from '@mui/icons-material/KeyOutlined';\nimport LoginOutlinedIcon from '@mui/icons-material/LoginOutlined';\nimport LogoutOutlinedIcon from '@mui/icons-material/LogoutOutlined';\nimport PasswordOutlinedIcon from '@mui/icons-material/PasswordOutlined';\nimport PhonelinkLockOutlinedIcon from '@mui/icons-material/PhonelinkLockOutlined';\nimport PhonelinkEraseOutlinedIcon from '@mui/icons-material/PhonelinkEraseOutlined';\nimport SyncLockOutlinedIcon from '@mui/icons-material/SyncLockOutlined';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport SectionCard from '@/ui/layout/SectionCard.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaActivityLog)\nconst THEME_NAME = 'CalcitaActivityLog';\nconst ActivityItemIcon = piecePart(THEME_NAME, 'ItemIcon', ListItemIcon);\n\n/** Los tipos de evento que el paquete conoce, con su texto en security:activity.kinds. */\nexport const ACCOUNT_EVENT_KIND = Object.freeze({\n signedIn: 'signedIn',\n signInFailed: 'signInFailed',\n signedOut: 'signedOut',\n passwordChanged: 'passwordChanged',\n twoStepEnabled: 'twoStepEnabled',\n twoStepDisabled: 'twoStepDisabled',\n passkeyAdded: 'passkeyAdded',\n passkeyRemoved: 'passkeyRemoved',\n recoveryCodesGenerated: 'recoveryCodesGenerated',\n recoveryCodeUsed: 'recoveryCodeUsed',\n sessionsClosed: 'sessionsClosed',\n});\n\n// el ícono de cada tipo, y en rojo lo que pide atención (un intento con contraseña equivocada, quitar protección)\nconst EVENT_LOOK = Object.freeze({\n [ACCOUNT_EVENT_KIND.signedIn]: {Icon: LoginOutlinedIcon},\n [ACCOUNT_EVENT_KIND.signInFailed]: {Icon: CancelOutlinedIcon, color: 'error'},\n [ACCOUNT_EVENT_KIND.signedOut]: {Icon: LogoutOutlinedIcon},\n [ACCOUNT_EVENT_KIND.passwordChanged]: {Icon: PasswordOutlinedIcon},\n [ACCOUNT_EVENT_KIND.twoStepEnabled]: {Icon: PhonelinkLockOutlinedIcon, color: 'success'},\n [ACCOUNT_EVENT_KIND.twoStepDisabled]: {Icon: PhonelinkEraseOutlinedIcon, color: 'warning'},\n [ACCOUNT_EVENT_KIND.passkeyAdded]: {Icon: FingerprintOutlinedIcon, color: 'success'},\n [ACCOUNT_EVENT_KIND.passkeyRemoved]: {Icon: FingerprintOutlinedIcon, color: 'warning'},\n [ACCOUNT_EVENT_KIND.recoveryCodesGenerated]: {Icon: KeyOutlinedIcon},\n [ACCOUNT_EVENT_KIND.recoveryCodeUsed]: {Icon: KeyOutlinedIcon, color: 'warning'},\n [ACCOUNT_EVENT_KIND.sessionsClosed]: {Icon: SyncLockOutlinedIcon},\n});\nconst DEFAULT_LOOK = Object.freeze({Icon: EventNoteOutlinedIcon});\nconst LOADING_HEIGHT_PIXELS = 160;\n\n/**\n * @param {object} props\n * @param {Array<{id: string, kind: string, occurredAt: string, device?: string|null, network?: string|null, description?: string}>|null} props.events\n * del más nuevo al más viejo; null mientras llegan\n * @param {string|null} [props.error] ya traducido si no se pudieron traer\n * @param {object} [props.texts] los textos que cambian, con la forma de `security` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"ActivityLog.js","sources":["../../src/security/ActivityLog.jsx"],"sourcesContent":["// La bitácora de la cuenta: lo último que pasó (entró, contraseña equivocada, cambió su contraseña, activó la verificación,\n// agregó una llave…), con la hora, el equipo y la red a medias. Para que la persona reconozca lo que no fue ella.\n//\n// Cada evento llega con su tipo (kind) y el texto sale de los textos del paquete (security:activity.kinds.<kind>); un tipo\n// propio del proyecto trae su texto en description.\n//\n// <ActivityLog events={events} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaActivityLog con defaultProps y\n// styleOverrides de sus partes: itemIcon.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Alert from '@mui/material/Alert';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';\nimport EventNoteOutlinedIcon from '@mui/icons-material/EventNoteOutlined';\nimport FingerprintOutlinedIcon from '@mui/icons-material/FingerprintOutlined';\nimport KeyOutlinedIcon from '@mui/icons-material/KeyOutlined';\nimport LoginOutlinedIcon from '@mui/icons-material/LoginOutlined';\nimport LogoutOutlinedIcon from '@mui/icons-material/LogoutOutlined';\nimport PasswordOutlinedIcon from '@mui/icons-material/PasswordOutlined';\nimport PhonelinkLockOutlinedIcon from '@mui/icons-material/PhonelinkLockOutlined';\nimport PhonelinkEraseOutlinedIcon from '@mui/icons-material/PhonelinkEraseOutlined';\nimport SyncLockOutlinedIcon from '@mui/icons-material/SyncLockOutlined';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport SectionCard from '@/ui/layout/SectionCard.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaActivityLog)\nconst THEME_NAME = 'CalcitaActivityLog';\nconst ActivityItemIcon = piecePart(THEME_NAME, 'ItemIcon', ListItemIcon);\n\n/** Los tipos de evento que el paquete conoce, con su texto en security:activity.kinds. */\nexport const ACCOUNT_EVENT_KIND = Object.freeze({\n signedIn: 'signedIn',\n signInFailed: 'signInFailed',\n signedOut: 'signedOut',\n passwordChanged: 'passwordChanged',\n twoStepEnabled: 'twoStepEnabled',\n twoStepDisabled: 'twoStepDisabled',\n passkeyAdded: 'passkeyAdded',\n passkeyRemoved: 'passkeyRemoved',\n recoveryCodesGenerated: 'recoveryCodesGenerated',\n recoveryCodeUsed: 'recoveryCodeUsed',\n sessionsClosed: 'sessionsClosed',\n});\n\n// el ícono de cada tipo, y en rojo lo que pide atención (un intento con contraseña equivocada, quitar protección)\nconst EVENT_LOOK = Object.freeze({\n [ACCOUNT_EVENT_KIND.signedIn]: {Icon: LoginOutlinedIcon},\n [ACCOUNT_EVENT_KIND.signInFailed]: {Icon: CancelOutlinedIcon, color: 'error'},\n [ACCOUNT_EVENT_KIND.signedOut]: {Icon: LogoutOutlinedIcon},\n [ACCOUNT_EVENT_KIND.passwordChanged]: {Icon: PasswordOutlinedIcon},\n [ACCOUNT_EVENT_KIND.twoStepEnabled]: {Icon: PhonelinkLockOutlinedIcon, color: 'success'},\n [ACCOUNT_EVENT_KIND.twoStepDisabled]: {Icon: PhonelinkEraseOutlinedIcon, color: 'warning'},\n [ACCOUNT_EVENT_KIND.passkeyAdded]: {Icon: FingerprintOutlinedIcon, color: 'success'},\n [ACCOUNT_EVENT_KIND.passkeyRemoved]: {Icon: FingerprintOutlinedIcon, color: 'warning'},\n [ACCOUNT_EVENT_KIND.recoveryCodesGenerated]: {Icon: KeyOutlinedIcon},\n [ACCOUNT_EVENT_KIND.recoveryCodeUsed]: {Icon: KeyOutlinedIcon, color: 'warning'},\n [ACCOUNT_EVENT_KIND.sessionsClosed]: {Icon: SyncLockOutlinedIcon},\n});\nconst DEFAULT_LOOK = Object.freeze({Icon: EventNoteOutlinedIcon});\nconst LOADING_HEIGHT_PIXELS = 160;\n\n/**\n * @param {object} props\n * @param {Array<{id: string, kind: string, occurredAt: string, device?: string|null, network?: string|null, description?: string}>|null} props.events\n * del más nuevo al más viejo; null mientras llegan\n * @param {string|null} [props.error] ya traducido si no se pudieron traer\n * @param {object} [props.texts] los textos que cambian, con la forma de `security` en los textos del paquete\n */\nfunction ActivityLogContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaActivityLog.defaultProps) y encima lo que llega aquí\n const {events, error = null, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('security', texts);\n const {formatDateTime} = useFormatters();\n\n return (\n <SectionCard title={t('activity.title')}>\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">{t('activity.description')}</Typography>\n {error && <Alert severity=\"error\">{error}</Alert>}\n {!events && !error && <Skeleton variant=\"rounded\" height={LOADING_HEIGHT_PIXELS} />}\n {events?.length === 0 && <Typography variant=\"body2\" color=\"text.secondary\">{t('activity.empty')}</Typography>}\n {events?.length > 0 && (\n <List dense disablePadding>\n {events.map((event) => {\n const look = EVENT_LOOK[event.kind] ?? DEFAULT_LOOK;\n const where = event.device && event.network ? ` · ${t('activity.from', {device: event.device, network: event.network})}` : '';\n return (\n <ListItem key={event.id} disableGutters>\n <ActivityItemIcon ownSx={{minWidth: 36}}><look.Icon fontSize=\"small\" color={look.color ?? 'action'} /></ActivityItemIcon>\n <ListItemText primary={event.description ?? t(`activity.kinds.${event.kind}`)}\n secondary={`${formatDateTime(event.occurredAt)}${where}`} />\n </ListItem>\n );\n })}\n </List>\n )}\n </Stack>\n </SectionCard>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst ActivityLog = withPieceSlots(ActivityLogContent, THEME_NAME);\nexport default ActivityLog;\n"],"names":[],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ import { saveBlob } from "../files.js";
|
|
|
16
16
|
import { useToolkitSettings } from "../toolkit/toolkitContext.js";
|
|
17
17
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
18
18
|
import CopyButton from "../ui/actions/CopyButton.js";
|
|
19
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
19
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
20
20
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
21
21
|
const THEME_NAME = "CalcitaRecoveryCodesDialog";
|
|
22
22
|
const RecoveryCodes = piecePart(THEME_NAME, "Codes", Box);
|
|
@@ -25,7 +25,7 @@ const CODE_FONT = "ui-monospace, SFMono-Regular, Menlo, monospace";
|
|
|
25
25
|
function todayText() {
|
|
26
26
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
27
27
|
}
|
|
28
|
-
function
|
|
28
|
+
function RecoveryCodesDialogContent(props) {
|
|
29
29
|
const { codes, account = "", onClose, onCodesSaved, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
30
30
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
31
31
|
const { t } = useToolkitTranslation("security", texts);
|
|
@@ -86,6 +86,7 @@ function RecoveryCodesDialog(props) {
|
|
|
86
86
|
] })
|
|
87
87
|
);
|
|
88
88
|
}
|
|
89
|
+
const RecoveryCodesDialog = withPieceSlots(RecoveryCodesDialogContent, THEME_NAME);
|
|
89
90
|
export {
|
|
90
91
|
RecoveryCodesDialog as default
|
|
91
92
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecoveryCodesDialog.js","sources":["../../src/security/RecoveryCodesDialog.jsx"],"sourcesContent":["// Los códigos de recuperación, que se enseñan UNA sola vez (el servidor solo guarda su huella, no los códigos).\n//\n// No se puede cerrar sin marcar «Ya los guardé»: es la única salida que tiene la persona el día que pierda su teléfono,\n// y un aviso que se cierra con Esc o con un clic afuera se cierra sin leer. Se pueden copiar todos o bajar en un .txt con\n// el nombre de la app, la cuenta y la fecha.\n//\n// <RecoveryCodesDialog codes={codes} account={session.email} onClose={() => setCodes(null)} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRecoveryCodesDialog con defaultProps y\n// styleOverrides de sus partes: codes, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Checkbox from '@mui/material/Checkbox';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport Stack from '@mui/material/Stack';\nimport DownloadIcon from '@mui/icons-material/Download';\n\nimport {saveBlob} from '@/files.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport CopyButton from '@/ui/actions/CopyButton.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaRecoveryCodesDialog)\nconst THEME_NAME = 'CalcitaRecoveryCodesDialog';\nconst RecoveryCodes = piecePart(THEME_NAME, 'Codes', Box);\nconst RecoveryActions = piecePart(THEME_NAME, 'Actions', Stack);\n\nconst CODE_FONT = 'ui-monospace, SFMono-Regular, Menlo, monospace';\n\n/**\n * La fecha de hoy como AAAA-MM-DD, para el nombre y el encabezado del archivo.\n *\n * @returns {string}\n */\nfunction todayText() {\n return new Date().toISOString().slice(0, 10);\n}\n\n/**\n * @param {object} props\n * @param {string[]|null} props.codes los códigos recién generados; sin códigos el diálogo está cerrado\n * @param {string} [props.account] la cuenta (normalmente el correo), para el archivo\n * @param {() => void} props.onClose solo se llama después de «Ya los guardé»\n * @param {(method: 'copy'|'download') => void} [props.onCodesSaved] la persona los copió o los descargó (para la bitácora de\n * seguridad)\n * @param {object} [props.texts] los textos que cambian, con la forma de `security` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"RecoveryCodesDialog.js","sources":["../../src/security/RecoveryCodesDialog.jsx"],"sourcesContent":["// Los códigos de recuperación, que se enseñan UNA sola vez (el servidor solo guarda su huella, no los códigos).\n//\n// No se puede cerrar sin marcar «Ya los guardé»: es la única salida que tiene la persona el día que pierda su teléfono,\n// y un aviso que se cierra con Esc o con un clic afuera se cierra sin leer. Se pueden copiar todos o bajar en un .txt con\n// el nombre de la app, la cuenta y la fecha.\n//\n// <RecoveryCodesDialog codes={codes} account={session.email} onClose={() => setCodes(null)} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaRecoveryCodesDialog con defaultProps y\n// styleOverrides de sus partes: codes, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Checkbox from '@mui/material/Checkbox';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport FormControlLabel from '@mui/material/FormControlLabel';\nimport Stack from '@mui/material/Stack';\nimport DownloadIcon from '@mui/icons-material/Download';\n\nimport {saveBlob} from '@/files.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport CopyButton from '@/ui/actions/CopyButton.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaRecoveryCodesDialog)\nconst THEME_NAME = 'CalcitaRecoveryCodesDialog';\nconst RecoveryCodes = piecePart(THEME_NAME, 'Codes', Box);\nconst RecoveryActions = piecePart(THEME_NAME, 'Actions', Stack);\n\nconst CODE_FONT = 'ui-monospace, SFMono-Regular, Menlo, monospace';\n\n/**\n * La fecha de hoy como AAAA-MM-DD, para el nombre y el encabezado del archivo.\n *\n * @returns {string}\n */\nfunction todayText() {\n return new Date().toISOString().slice(0, 10);\n}\n\n/**\n * @param {object} props\n * @param {string[]|null} props.codes los códigos recién generados; sin códigos el diálogo está cerrado\n * @param {string} [props.account] la cuenta (normalmente el correo), para el archivo\n * @param {() => void} props.onClose solo se llama después de «Ya los guardé»\n * @param {(method: 'copy'|'download') => void} [props.onCodesSaved] la persona los copió o los descargó (para la bitácora de\n * seguridad)\n * @param {object} [props.texts] los textos que cambian, con la forma de `security` en los textos del paquete\n */\nfunction RecoveryCodesDialogContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaRecoveryCodesDialog.defaultProps) y encima lo que llega aquí\n const {codes, account = '', onClose, onCodesSaved, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('security', texts);\n const {appName} = useToolkitSettings();\n const [isSaved, setIsSaved] = useState(false);\n const isOpen = Boolean(codes?.length);\n\n const download = () => {\n const date = todayText();\n const lines = [\n t('recoveryCodes.fileTitle', {appName}),\n t('recoveryCodes.fileAccount', {account}),\n t('recoveryCodes.fileGenerated', {date}),\n '',\n t('recoveryCodes.fileHowTo'),\n t('recoveryCodes.fileKeepSafe'),\n '',\n ...codes,\n '',\n ];\n saveBlob(new Blob([lines.join('\\n')], {type: 'text/plain;charset=utf-8'}), t('recoveryCodes.fileName', {date}));\n onCodesSaved?.('download');\n };\n\n const close = () => {\n setIsSaved(false);\n onClose();\n };\n\n return (\n // ni Esc ni un clic afuera lo cierran: solo «Listo», después de marcar que los guardó\n <Dialog open={isOpen} maxWidth=\"xs\" fullWidth disableEscapeKeyDown onClose={() => {}}>\n <DialogTitle>{t('recoveryCodes.title')}</DialogTitle>\n <DialogContent>\n <Stack spacing={2}>\n <Alert severity=\"warning\">{t('recoveryCodes.warning')}</Alert>\n <RecoveryCodes component=\"ol\" ownSx={{\n m: 0, p: 2, pl: 5, borderRadius: 2, bgcolor: 'action.hover', fontFamily: CODE_FONT, fontSize: 15,\n columns: 2, columnGap: 4,\n }}>\n {(codes ?? []).map((code) => <li key={code}>{code}</li>)}\n </RecoveryCodes>\n <RecoveryActions direction=\"row\" spacing={1} ownSx={{alignItems: 'center'}}>\n <CopyButton value={(codes ?? []).join('\\n')} label={t('recoveryCodes.copyAll')} onCopied={() => onCodesSaved?.('copy')} />\n <Button size=\"small\" startIcon={<DownloadIcon />} onClick={download}>{t('recoveryCodes.download')}</Button>\n </RecoveryActions>\n <FormControlLabel control={<Checkbox checked={isSaved} onChange={(event) => setIsSaved(event.target.checked)} />}\n label={t('recoveryCodes.saved')} />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button variant=\"contained\" disabled={!isSaved} onClick={close}>{t('recoveryCodes.done')}</Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst RecoveryCodesDialog = withPieceSlots(RecoveryCodesDialogContent, THEME_NAME);\nexport default RecoveryCodesDialog;\n"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@ import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
|
20
20
|
import Tag from "../ui/labels/Tag.js";
|
|
21
21
|
import SectionCard from "../ui/layout/SectionCard.js";
|
|
22
22
|
import ComponentTexts from "../toolkit/ComponentTexts.js";
|
|
23
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
23
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
24
24
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
25
25
|
const THEME_NAME = "CalcitaSessionsCard";
|
|
26
26
|
const SessionsItem = piecePart(THEME_NAME, "Item", ListItem);
|
|
@@ -52,7 +52,7 @@ function SessionRow({ session }) {
|
|
|
52
52
|
] })
|
|
53
53
|
);
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function SessionsCardContent(props) {
|
|
56
56
|
const { sessions, error = null, onCloseOthers, onClosed, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
57
57
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
58
58
|
const { t } = useToolkitTranslation("security", texts);
|
|
@@ -90,6 +90,7 @@ function SessionsCard(props) {
|
|
|
90
90
|
)
|
|
91
91
|
] }) });
|
|
92
92
|
}
|
|
93
|
+
const SessionsCard = withPieceSlots(SessionsCardContent, THEME_NAME);
|
|
93
94
|
export {
|
|
94
95
|
SessionsCard as default
|
|
95
96
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionsCard.js","sources":["../../src/security/SessionsCard.jsx"],"sourcesContent":["// «Mis sesiones»: los equipos donde la cuenta sigue abierta (este, marcado) y «Cerrar la sesión en los demás equipos».\n//\n// No pide la contraseña: cerrar sesiones no da acceso a nada, solo lo quita. Nunca se pinta la IP completa ni el user\n// agent: el servidor manda el navegador, el sistema y la red a medias (p. ej. «187.190.x.x»), que bastan para reconocer\n// un equipo sin exponer datos.\n//\n// <SessionsCard sessions={sessions} onCloseOthers={async () => (await api.post('/sessions/close-others')).closed} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSessionsCard con defaultProps y\n// styleOverrides de sus partes: item, itemIcon, itemHeader, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport Divider from '@mui/material/Divider';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport ComputerOutlinedIcon from '@mui/icons-material/ComputerOutlined';\nimport PhoneIphoneOutlinedIcon from '@mui/icons-material/PhoneIphoneOutlined';\n\nimport ConfirmDialog from '@/feedback/ConfirmDialog.jsx';\nimport {toastSuccess} from '@/feedback/toast.jsx';\nimport {useConfirmAction} from '@/feedback/useConfirmAction.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Tag from '@/ui/labels/Tag.jsx';\nimport SectionCard from '@/ui/layout/SectionCard.jsx';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaSessionsCard)\nconst THEME_NAME = 'CalcitaSessionsCard';\nconst SessionsItem = piecePart(THEME_NAME, 'Item', ListItem);\nconst SessionsItemIcon = piecePart(THEME_NAME, 'ItemIcon', ListItemIcon);\nconst SessionsItemHeader = piecePart(THEME_NAME, 'ItemHeader', Stack);\nconst SessionsActions = piecePart(THEME_NAME, 'Actions', Stack);\n\nconst LOADING_HEIGHT_PIXELS = 96;\n\n/**\n * Un renglón: el equipo (navegador en sistema), desde cuándo y su red a medias.\n *\n * @param {{session: object}} props\n */\nfunction SessionRow({session}) {\n const {t} = useToolkitTranslation('security');\n const {formatDateTime} = useFormatters();\n const DeviceIcon = session.isMobile ? PhoneIphoneOutlinedIcon : ComputerOutlinedIcon;\n const device = t('sessions.device', {\n browser: session.browser ?? t('sessions.otherBrowser'), platform: session.platform ?? t('sessions.otherPlatform'),\n });\n const details = [t('sessions.since', {date: formatDateTime(session.startedAt)})];\n if (session.network) {\n details.push(t('sessions.network', {network: session.network}));\n }\n\n return (\n // component=\"div\": el <li> lo pone la lista de afuera (con su divisor); un ListItem es otro <li> y un <li> dentro de\n // otro no es HTML válido (React lo marcaba en consola)\n <SessionsItem component=\"div\" disableGutters alignItems=\"flex-start\" ownSx={{gap: 1}}>\n <SessionsItemIcon ownSx={{minWidth: 36, mt: 0.5}}><DeviceIcon /></SessionsItemIcon>\n <ListItemText primary={(\n <SessionsItemHeader direction=\"row\" spacing={1} useFlexGap ownSx={{alignItems: 'center', flexWrap: 'wrap'}}>\n <span>{device}</span>\n {session.isCurrent && <Tag label={t('sessions.current')} color=\"success\" />}\n </SessionsItemHeader>\n )} secondary={details.join(' · ')} slotProps={{primary: {sx: {fontWeight: 600}}}} />\n </SessionsItem>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, browser?: string|null, platform?: string|null, isMobile?: boolean, startedAt: string, network?: string|null, isCurrent?: boolean}>|null} props.sessions\n * null mientras llegan\n * @param {string|null} [props.error] ya traducido (ApiError.message) si no se pudieron traer\n * @param {() => Promise<number>} props.onCloseOthers cierra las demás; regresa cuántas cerró\n * @param {() => void} [props.onClosed] p. ej. volver a pedir la lista\n * @param {object} [props.texts] los textos que cambian, con la forma de `security` en los textos del paquete\n */\nexport default function SessionsCard(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSessionsCard.defaultProps) y encima lo que llega aquí\n const {sessions, error = null, onCloseOthers, onClosed, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('security', texts);\n const othersCount = (sessions ?? []).filter((session) => !session.isCurrent).length;\n const confirmation = useConfirmAction({\n run: () => onCloseOthers(),\n onDone: (closedCount) => {\n toastSuccess(closedCount ? t('sessions.closed', {count: closedCount}) : t('sessions.noneClosed'));\n onClosed?.();\n },\n });\n\n return (\n <ComponentTexts namespace=\"security\" texts={texts}>\n <SectionCard title={t('sessions.title')}>\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">{t('sessions.description')}</Typography>\n {error && <Alert severity=\"error\">{error}</Alert>}\n {!sessions && !error && <Skeleton variant=\"rounded\" height={LOADING_HEIGHT_PIXELS} />}\n {sessions && (\n <List disablePadding>\n {sessions.map((session, index) => (\n <li key={session.id}>\n {index > 0 && <Divider component=\"div\" />}\n <SessionRow session={session} />\n </li>\n ))}\n </List>\n )}\n <SessionsActions spacing={1} ownSx={{alignItems: 'flex-start'}}>\n {sessions && othersCount === 0 && <Typography variant=\"caption\" color=\"text.secondary\">{t('sessions.onlyThis')}</Typography>}\n <Button variant=\"outlined\" color=\"warning\" disabled={!sessions || othersCount === 0} onClick={() => confirmation.open({})}>\n {t('sessions.closeOthers')}\n </Button>\n </SessionsActions>\n </Stack>\n <ConfirmDialog {...confirmation.dialogProps} tone=\"warning\" title={t('sessions.confirmTitle')}\n description={t('sessions.confirmText', {count: othersCount})} confirmLabel={t('sessions.confirm')} />\n </SectionCard>\n </ComponentTexts>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionsCard.js","sources":["../../src/security/SessionsCard.jsx"],"sourcesContent":["// «Mis sesiones»: los equipos donde la cuenta sigue abierta (este, marcado) y «Cerrar la sesión en los demás equipos».\n//\n// No pide la contraseña: cerrar sesiones no da acceso a nada, solo lo quita. Nunca se pinta la IP completa ni el user\n// agent: el servidor manda el navegador, el sistema y la red a medias (p. ej. «187.190.x.x»), que bastan para reconocer\n// un equipo sin exponer datos.\n//\n// <SessionsCard sessions={sessions} onCloseOthers={async () => (await api.post('/sessions/close-others')).closed} />\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSessionsCard con defaultProps y\n// styleOverrides de sus partes: item, itemIcon, itemHeader, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport Divider from '@mui/material/Divider';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Skeleton from '@mui/material/Skeleton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport ComputerOutlinedIcon from '@mui/icons-material/ComputerOutlined';\nimport PhoneIphoneOutlinedIcon from '@mui/icons-material/PhoneIphoneOutlined';\n\nimport ConfirmDialog from '@/feedback/ConfirmDialog.jsx';\nimport {toastSuccess} from '@/feedback/toast.jsx';\nimport {useConfirmAction} from '@/feedback/useConfirmAction.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Tag from '@/ui/labels/Tag.jsx';\nimport SectionCard from '@/ui/layout/SectionCard.jsx';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaSessionsCard)\nconst THEME_NAME = 'CalcitaSessionsCard';\nconst SessionsItem = piecePart(THEME_NAME, 'Item', ListItem);\nconst SessionsItemIcon = piecePart(THEME_NAME, 'ItemIcon', ListItemIcon);\nconst SessionsItemHeader = piecePart(THEME_NAME, 'ItemHeader', Stack);\nconst SessionsActions = piecePart(THEME_NAME, 'Actions', Stack);\n\nconst LOADING_HEIGHT_PIXELS = 96;\n\n/**\n * Un renglón: el equipo (navegador en sistema), desde cuándo y su red a medias.\n *\n * @param {{session: object}} props\n */\nfunction SessionRow({session}) {\n const {t} = useToolkitTranslation('security');\n const {formatDateTime} = useFormatters();\n const DeviceIcon = session.isMobile ? PhoneIphoneOutlinedIcon : ComputerOutlinedIcon;\n const device = t('sessions.device', {\n browser: session.browser ?? t('sessions.otherBrowser'), platform: session.platform ?? t('sessions.otherPlatform'),\n });\n const details = [t('sessions.since', {date: formatDateTime(session.startedAt)})];\n if (session.network) {\n details.push(t('sessions.network', {network: session.network}));\n }\n\n return (\n // component=\"div\": el <li> lo pone la lista de afuera (con su divisor); un ListItem es otro <li> y un <li> dentro de\n // otro no es HTML válido (React lo marcaba en consola)\n <SessionsItem component=\"div\" disableGutters alignItems=\"flex-start\" ownSx={{gap: 1}}>\n <SessionsItemIcon ownSx={{minWidth: 36, mt: 0.5}}><DeviceIcon /></SessionsItemIcon>\n <ListItemText primary={(\n <SessionsItemHeader direction=\"row\" spacing={1} useFlexGap ownSx={{alignItems: 'center', flexWrap: 'wrap'}}>\n <span>{device}</span>\n {session.isCurrent && <Tag label={t('sessions.current')} color=\"success\" />}\n </SessionsItemHeader>\n )} secondary={details.join(' · ')} slotProps={{primary: {sx: {fontWeight: 600}}}} />\n </SessionsItem>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, browser?: string|null, platform?: string|null, isMobile?: boolean, startedAt: string, network?: string|null, isCurrent?: boolean}>|null} props.sessions\n * null mientras llegan\n * @param {string|null} [props.error] ya traducido (ApiError.message) si no se pudieron traer\n * @param {() => Promise<number>} props.onCloseOthers cierra las demás; regresa cuántas cerró\n * @param {() => void} [props.onClosed] p. ej. volver a pedir la lista\n * @param {object} [props.texts] los textos que cambian, con la forma de `security` en los textos del paquete\n */\nfunction SessionsCardContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSessionsCard.defaultProps) y encima lo que llega aquí\n const {sessions, error = null, onCloseOthers, onClosed, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('security', texts);\n const othersCount = (sessions ?? []).filter((session) => !session.isCurrent).length;\n const confirmation = useConfirmAction({\n run: () => onCloseOthers(),\n onDone: (closedCount) => {\n toastSuccess(closedCount ? t('sessions.closed', {count: closedCount}) : t('sessions.noneClosed'));\n onClosed?.();\n },\n });\n\n return (\n <ComponentTexts namespace=\"security\" texts={texts}>\n <SectionCard title={t('sessions.title')}>\n <Stack spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">{t('sessions.description')}</Typography>\n {error && <Alert severity=\"error\">{error}</Alert>}\n {!sessions && !error && <Skeleton variant=\"rounded\" height={LOADING_HEIGHT_PIXELS} />}\n {sessions && (\n <List disablePadding>\n {sessions.map((session, index) => (\n <li key={session.id}>\n {index > 0 && <Divider component=\"div\" />}\n <SessionRow session={session} />\n </li>\n ))}\n </List>\n )}\n <SessionsActions spacing={1} ownSx={{alignItems: 'flex-start'}}>\n {sessions && othersCount === 0 && <Typography variant=\"caption\" color=\"text.secondary\">{t('sessions.onlyThis')}</Typography>}\n <Button variant=\"outlined\" color=\"warning\" disabled={!sessions || othersCount === 0} onClick={() => confirmation.open({})}>\n {t('sessions.closeOthers')}\n </Button>\n </SessionsActions>\n </Stack>\n <ConfirmDialog {...confirmation.dialogProps} tone=\"warning\" title={t('sessions.confirmTitle')}\n description={t('sessions.confirmText', {count: othersCount})} confirmLabel={t('sessions.confirm')} />\n </SectionCard>\n </ComponentTexts>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst SessionsCard = withPieceSlots(SessionsCardContent, THEME_NAME);\nexport default SessionsCard;\n"],"names":[],"mappings":"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"}
|
package/dist/table.js
CHANGED
|
@@ -6,7 +6,12 @@ import { default as default6 } from "./DataTable/GoToPage.js";
|
|
|
6
6
|
import { default as default7 } from "./DataTable/ServerTable.js";
|
|
7
7
|
import { default as default8 } from "./DataTable/ServerTableFilterDrawer.js";
|
|
8
8
|
import { buildChipConfig } from "./DataTable/buildChipConfig.js";
|
|
9
|
-
import { detailColumn, facetColumn, textColumn } from "./DataTable/columns.js";
|
|
9
|
+
import { ACTIONS_COLUMN_FIELD, DETAIL_COLUMN_FIELD, actionsColumn, detailColumn, facetColumn, stackedColumn, statusColumn, textColumn } from "./DataTable/columns.js";
|
|
10
|
+
import { default as default9 } from "./DataTable/StackedCell.js";
|
|
11
|
+
import { statusColumnWidthOf, textWidthOf } from "./DataTable/columnWidths.js";
|
|
12
|
+
import { fixedColumnOffsetsOf, fixedFieldsOf } from "./DataTable/fixedColumns.js";
|
|
13
|
+
import { default as default10, TableViewToggle } from "./DataTable/TableCardsView.js";
|
|
14
|
+
import { TABLE_VIEW, useTableView } from "./DataTable/useTableView.js";
|
|
10
15
|
import { TableFilterContext, useTableFilter } from "./DataTable/filterContext.js";
|
|
11
16
|
import { toServerTableParams } from "./DataTable/serverTableParams.js";
|
|
12
17
|
import { buildTableActors, tableMachine } from "./DataTable/tableMachine.js";
|
|
@@ -14,8 +19,10 @@ import { useDebouncedFilter } from "./DataTable/useDebouncedFilter.js";
|
|
|
14
19
|
import { FILTER_KINDS, isFilterActive, useTableState } from "./DataTable/useTableState.js";
|
|
15
20
|
import { SELECTION_PHONE_LAYOUT } from "./ui/actions/SelectionActions.js";
|
|
16
21
|
export {
|
|
22
|
+
ACTIONS_COLUMN_FIELD,
|
|
17
23
|
default2 as AuditTrailDialog,
|
|
18
24
|
default3 as AuditTrailTable,
|
|
25
|
+
DETAIL_COLUMN_FIELD,
|
|
19
26
|
default4 as EmptyState,
|
|
20
27
|
FILTER_KINDS,
|
|
21
28
|
default5 as FilterChips,
|
|
@@ -23,17 +30,29 @@ export {
|
|
|
23
30
|
SELECTION_PHONE_LAYOUT,
|
|
24
31
|
default7 as ServerTable,
|
|
25
32
|
default8 as ServerTableFilterDrawer,
|
|
33
|
+
default9 as StackedCell,
|
|
34
|
+
TABLE_VIEW,
|
|
35
|
+
default10 as TableCardsView,
|
|
26
36
|
TableFilterContext,
|
|
37
|
+
TableViewToggle,
|
|
38
|
+
actionsColumn,
|
|
27
39
|
buildChipConfig,
|
|
28
40
|
buildTableActors,
|
|
29
41
|
detailColumn,
|
|
30
42
|
facetColumn,
|
|
43
|
+
fixedColumnOffsetsOf,
|
|
44
|
+
fixedFieldsOf,
|
|
31
45
|
isFilterActive,
|
|
46
|
+
stackedColumn,
|
|
47
|
+
statusColumn,
|
|
48
|
+
statusColumnWidthOf,
|
|
32
49
|
tableMachine,
|
|
33
50
|
textColumn,
|
|
51
|
+
textWidthOf,
|
|
34
52
|
toServerTableParams,
|
|
35
53
|
useDebouncedFilter,
|
|
36
54
|
useTableFilter,
|
|
37
|
-
useTableState
|
|
55
|
+
useTableState,
|
|
56
|
+
useTableView
|
|
38
57
|
};
|
|
39
58
|
//# sourceMappingURL=table.js.map
|
package/dist/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"table.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -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":"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"}
|
|
@@ -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;"}
|