@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":"DateTimeInput.js","sources":["../../src/datePickers/DateTimeInput.jsx"],"sourcesContent":["// Campo de FECHA CON HORA suelto con el DateTimePicker de MUI X (@stackcraftmx/calcita/date-pickers), para un instante:\n// una cita, un envío programado, «posponer hasta».\n//\n// Viaja como instante en UTC ('2026-11-01T18:30:00Z') y se pinta en la zona del proveedor (ToolkitProvider timeZone),\n// rotulada abajo con su nombre («Hora estándar central»): quien lo ve sabe en qué hora está escribiendo aunque su equipo\n// esté en otra zona. Lee también lo que llega del servidor con desfase ('…-06:00'). '' = vacío. La hora sale de 24 horas o\n// con a.m. y p.m. según el hourCycle del proveedor.\n//\n// Los minutos van de uno en uno (minutesStep los cambia). Para «al menos N minutos después de ahora» (publicar con\n// anticipación) está minimumMinutesFromNow: el mínimo se recorre solo con el reloj mientras la pantalla está abierta. Para\n// «el fin, al menos N minutos después del inicio», minDateTime={instantPlusMinutes(startsAt, N)}. Con formik, las mismas\n// reglas en yup: minutesFromNowRule y minutesAfterRule (el servidor vuelve a revisar).\n//\n// Para una hora del día sin fecha («abre a las 09:00») va TimeInput; para una fecha sin hora, DateInput.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {DateTimePicker} from '@mui/x-date-pickers/DateTimePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {\n DEFAULT_MINUTES_STEP, earliestInstantFromNow, instantTextOf, latestInstantOf, pickerInstantOf, timeZoneNameOf,\n} from '@/datePickers/pickerValues.js';\nimport {useCurrentMinute} from '@/datePickers/useCurrentMinute.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {intlLocaleOf} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// entre la ayuda de la pantalla y el nombre de la zona\nconst HELPER_SEPARATOR = ' · ';\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value el instante ('2026-11-01T18:30:00Z' o con desfase) o ''\n * @param {(value: string) => void} props.onChange recibe el instante en UTC ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida; va antes del nombre de la zona\n * @param {string} [props.minDateTime] instante (el fin de algo: instantPlusMinutes(startsAt, 30))\n * @param {number} [props.minimumMinutesFromNow] al menos tantos minutos después de ahora; se recorre con el reloj\n * @param {string} [props.maxDateTime] instante\n * @param {boolean} [props.disableFuture]\n * @param {boolean} [props.disablePast] p. ej. un envío programado\n * @param {number} [props.minutesStep] de cuánto en cuánto van los minutos; por omisión de uno en uno (15 para citas de\n * cuarto de hora)\n * @param {string} [props.timeZone] otra zona que la del proveedor (la de una sucursal); rotulada igual\n * @param {boolean} [props.showTimeZone] false quita el nombre de la zona de abajo (por omisión se ve)\n * @param {string} [props.timeZoneLabel] ya traducido, en lugar del nombre que da el navegador\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DateTimePicker de MUI X que aquí no tiene nombre (views, shouldDisableDate,\n * slots, slotProps…); value y onChange son del campo\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"DateTimeInput.js","sources":["../../src/datePickers/DateTimeInput.jsx"],"sourcesContent":["// Campo de FECHA CON HORA suelto con el DateTimePicker de MUI X (@stackcraftmx/calcita/date-pickers), para un instante:\n// una cita, un envío programado, «posponer hasta».\n//\n// Viaja como instante en UTC ('2026-11-01T18:30:00Z') y se pinta en la zona del proveedor (ToolkitProvider timeZone),\n// rotulada abajo con su nombre («Hora estándar central»): quien lo ve sabe en qué hora está escribiendo aunque su equipo\n// esté en otra zona. Lee también lo que llega del servidor con desfase ('…-06:00'). '' = vacío. La hora sale de 24 horas o\n// con a.m. y p.m. según el hourCycle del proveedor.\n//\n// Los minutos van de uno en uno (minutesStep los cambia). Para «al menos N minutos después de ahora» (publicar con\n// anticipación) está minimumMinutesFromNow: el mínimo se recorre solo con el reloj mientras la pantalla está abierta. Para\n// «el fin, al menos N minutos después del inicio», minDateTime={instantPlusMinutes(startsAt, N)}. Con formik, las mismas\n// reglas en yup: minutesFromNowRule y minutesAfterRule (el servidor vuelve a revisar).\n//\n// Para una hora del día sin fecha («abre a las 09:00») va TimeInput; para una fecha sin hora, DateInput.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDateTimeInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {DateTimePicker} from '@mui/x-date-pickers/DateTimePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {\n DEFAULT_MINUTES_STEP, earliestInstantFromNow, instantTextOf, latestInstantOf, pickerInstantOf, timeZoneNameOf,\n} from '@/datePickers/pickerValues.js';\nimport {useCurrentMinute} from '@/datePickers/useCurrentMinute.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {intlLocaleOf} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDateTimeInput)\nconst THEME_NAME = 'CalcitaDateTimeInput';\nconst DateTimeInputPicker = piecePart(THEME_NAME, 'Picker', DateTimePicker);\n\n// entre la ayuda de la pantalla y el nombre de la zona\nconst HELPER_SEPARATOR = ' · ';\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value el instante ('2026-11-01T18:30:00Z' o con desfase) o ''\n * @param {(value: string) => void} props.onChange recibe el instante en UTC ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ya traducida; va antes del nombre de la zona\n * @param {string} [props.minDateTime] instante (el fin de algo: instantPlusMinutes(startsAt, 30))\n * @param {number} [props.minimumMinutesFromNow] al menos tantos minutos después de ahora; se recorre con el reloj\n * @param {string} [props.maxDateTime] instante\n * @param {boolean} [props.disableFuture]\n * @param {boolean} [props.disablePast] p. ej. un envío programado\n * @param {number} [props.minutesStep] de cuánto en cuánto van los minutos; por omisión de uno en uno (15 para citas de\n * cuarto de hora)\n * @param {string} [props.timeZone] otra zona que la del proveedor (la de una sucursal); rotulada igual\n * @param {boolean} [props.showTimeZone] false quita el nombre de la zona de abajo (por omisión se ve)\n * @param {string} [props.timeZoneLabel] ya traducido, en lugar del nombre que da el navegador\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DateTimePicker de MUI X que aquí no tiene nombre (views, shouldDisableDate,\n * slots, slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction DateTimeInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText, minDateTime, minimumMinutesFromNow, maxDateTime, disableFuture = false,\n disablePast = false, minutesStep = DEFAULT_MINUTES_STEP, timeZone, showTimeZone = true, timeZoneLabel, disabled = false, inputRef, pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const field = usePickerField({name, kind: 'dateTime', error, label, messages, texts});\n const {languages} = useToolkitSettings();\n const shownTimeZone = timeZone ?? field.timeZone;\n const zoneText = showTimeZone ? (timeZoneLabel ?? timeZoneNameOf(shownTimeZone, intlLocaleOf(field.language, languages))) : null;\n const helperLine = [helperText, zoneText].filter(Boolean).join(HELPER_SEPARATOR) || ' ';\n // el mínimo que de verdad aplica: el que da la pantalla o el de «ahora + N minutos», el más tarde\n const hasClockRule = minimumMinutesFromNow != null;\n const currentMinute = useCurrentMinute(hasClockRule);\n const earliest = latestInstantOf(minDateTime, hasClockRule ? earliestInstantFromNow(minimumMinutesFromNow, currentMinute) : '');\n\n return (\n <PickerFrame inputId={field.inputId} label={label} language={field.language}>\n <DateTimeInputPicker\n {...pickerProps}\n timezone={shownTimeZone}\n format={field.formats.dateTime}\n ampm={field.formats.ampm}\n value={pickerInstantOf(value, shownTimeZone)}\n minDateTime={pickerInstantOf(earliest, shownTimeZone) ?? undefined}\n maxDateTime={pickerInstantOf(maxDateTime, shownTimeZone) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n minutesStep={minutesStep}\n timeSteps={{minutes: minutesStep, ...pickerProps?.timeSteps}}\n inputRef={inputRef}\n onChange={(pickedValue) => onChange(instantTextOf(pickedValue))}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf(\n {id: field.inputId, name, onBlur, error: Boolean(field.errorText), helperText: field.errorText ?? helperLine},\n pickerProps,\n )}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst DateTimeInput = withPieceSlots(DateTimeInputContent, THEME_NAME);\nexport default DateTimeInput;\n"],"names":[],"mappings":";;;;;;;;;;;AAiCA,MAAM,aAAa;AACnB,MAAM,sBAAsB,UAAU,YAAY,UAAU,cAAc;AAG1E,MAAM,mBAAmB;AA6BzB,SAAS,qBAAqB,OAAO;AACjC,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAY;AAAA,IAAa;AAAA,IAAuB;AAAA,IAAa,gBAAgB;AAAA,IAC1H,cAAc;AAAA,IAAO,cAAc;AAAA,IAAsB;AAAA,IAAU,eAAe;AAAA,IAAM;AAAA,IAAe,WAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAAa;AAAA,IAAU;AAAA,IAC1J,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,eAAe,EAAC,MAAM,MAAM,YAAY,OAAO,OAAO,UAAU,OAAM;AACpF,QAAM,EAAC,UAAA,IAAa,mBAAA;AACpB,QAAM,gBAAgB,YAAY,MAAM;AACxC,QAAM,WAAW,eAAgB,iBAAiB,eAAe,eAAe,aAAa,MAAM,UAAU,SAAS,CAAC,IAAK;AAC5H,QAAM,aAAa,CAAC,YAAY,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,gBAAgB,KAAK;AAEpF,QAAM,eAAe,yBAAyB;AAC9C,QAAM,gBAAgB,iBAAiB,YAAY;AACnD,QAAM,WAAW,gBAAgB,aAAa,eAAe,uBAAuB,uBAAuB,aAAa,IAAI,EAAE;AAE9H,SACI,oBAAC,eAAY,SAAS,MAAM,SAAS,OAAc,UAAU,MAAM,UAC/D,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MACJ,UAAU;AAAA,MACV,QAAQ,MAAM,QAAQ;AAAA,MACtB,MAAM,MAAM,QAAQ;AAAA,MACpB,OAAO,gBAAgB,OAAO,aAAa;AAAA,MAC3C,aAAa,gBAAgB,UAAU,aAAa,KAAK;AAAA,MACzD,aAAa,gBAAgB,aAAa,aAAa,KAAK;AAAA,MAC5D;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,EAAC,SAAS,aAAa,GAAG,aAAa,UAAA;AAAA,MAClD;AAAA,MACA,UAAU,CAAC,gBAAgB,SAAS,cAAc,WAAW,CAAC;AAAA,MAC9D,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACP,EAAC,IAAI,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS,GAAG,YAAY,MAAM,aAAa,WAAA;AAAA,QAClG;AAAA,MAAA;AAAA,IACJ;AAAA,EAAA,GAER;AAER;AAGA,MAAM,gBAAgB,eAAe,sBAAsB,UAAU;"}
|
|
@@ -5,29 +5,34 @@ import PickerFrame, { textFieldSlotPropsOf } from "./PickerFrame.js";
|
|
|
5
5
|
import { monthTextOf, pickerMonthOf } from "./pickerValues.js";
|
|
6
6
|
import { usePickerField } from "./usePickerField.js";
|
|
7
7
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
8
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
9
|
+
const THEME_NAME = "CalcitaMonthInput";
|
|
10
|
+
const MonthInputPicker = piecePart(THEME_NAME, "Picker", DatePicker);
|
|
8
11
|
const MONTH_VIEWS = ["year", "month"];
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
function MonthInputContent(props) {
|
|
13
|
+
const {
|
|
14
|
+
name,
|
|
15
|
+
label,
|
|
16
|
+
value,
|
|
17
|
+
onChange,
|
|
18
|
+
onBlur,
|
|
19
|
+
error,
|
|
20
|
+
helperText = " ",
|
|
21
|
+
minMonth,
|
|
22
|
+
maxMonth,
|
|
23
|
+
disableFuture = false,
|
|
24
|
+
disablePast = false,
|
|
25
|
+
disabled = false,
|
|
26
|
+
inputRef,
|
|
27
|
+
pickerProps,
|
|
28
|
+
messages,
|
|
29
|
+
texts,
|
|
30
|
+
...unknownProps
|
|
31
|
+
} = usePieceProps(props, THEME_NAME);
|
|
32
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
33
|
+
const field = usePickerField({ name, kind: "month", error, label, messages, texts });
|
|
29
34
|
return /* @__PURE__ */ jsx(PickerFrame, { inputId: field.inputId, label, language: field.language, children: /* @__PURE__ */ jsx(
|
|
30
|
-
|
|
35
|
+
MonthInputPicker,
|
|
31
36
|
{
|
|
32
37
|
...pickerProps,
|
|
33
38
|
views: MONTH_VIEWS,
|
|
@@ -52,6 +57,7 @@ function MonthInput({
|
|
|
52
57
|
}
|
|
53
58
|
) });
|
|
54
59
|
}
|
|
60
|
+
const MonthInput = withPieceSlots(MonthInputContent, THEME_NAME);
|
|
55
61
|
export {
|
|
56
62
|
MonthInput as default
|
|
57
63
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonthInput.js","sources":["../../src/datePickers/MonthInput.jsx"],"sourcesContent":["// Campo de MES suelto con el DatePicker de MUI X en meses y años (@stackcraftmx/calcita/date-pickers): el mes de un\n// reporte, de un saldo, de un cierre.\n//\n// Viaja 'AAAA-MM' en texto y se ve con el nombre del mes en el idioma activo («noviembre 2026»). Abre en los meses del año\n// que ya tiene; el año se cambia arriba. '' = vacío. Como la fecha, no se pasa por Date (que lo movería de mes según la\n// zona el día 1).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {DatePicker} from '@mui/x-date-pickers/DatePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {monthTextOf, pickerMonthOf} from '@/datePickers/pickerValues.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// primero el año y luego el mes: el selector abre en los meses y el año se cambia con su botón de arriba\nconst MONTH_VIEWS = ['year', 'month'];\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value 'AAAA-MM' o ''\n * @param {(value: string) => void} props.onChange recibe 'AAAA-MM' ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minMonth] 'AAAA-MM'\n * @param {string} [props.maxMonth] 'AAAA-MM'\n * @param {boolean} [props.disableFuture] p. ej. el mes de un reporte que ya cerró\n * @param {boolean} [props.disablePast]\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DatePicker de MUI X que aquí no tiene nombre (shouldDisableMonth, slots,\n * slotProps…); value y onChange son del campo\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"MonthInput.js","sources":["../../src/datePickers/MonthInput.jsx"],"sourcesContent":["// Campo de MES suelto con el DatePicker de MUI X en meses y años (@stackcraftmx/calcita/date-pickers): el mes de un\n// reporte, de un saldo, de un cierre.\n//\n// Viaja 'AAAA-MM' en texto y se ve con el nombre del mes en el idioma activo («noviembre 2026»). Abre en los meses del año\n// que ya tiene; el año se cambia arriba. '' = vacío. Como la fecha, no se pasa por Date (que lo movería de mes según la\n// zona el día 1).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaMonthInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {DatePicker} from '@mui/x-date-pickers/DatePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {monthTextOf, pickerMonthOf} from '@/datePickers/pickerValues.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaMonthInput)\nconst THEME_NAME = 'CalcitaMonthInput';\nconst MonthInputPicker = piecePart(THEME_NAME, 'Picker', DatePicker);\n\n// primero el año y luego el mes: el selector abre en los meses y el año se cambia con su botón de arriba\nconst MONTH_VIEWS = ['year', 'month'];\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value 'AAAA-MM' o ''\n * @param {(value: string) => void} props.onChange recibe 'AAAA-MM' ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minMonth] 'AAAA-MM'\n * @param {string} [props.maxMonth] 'AAAA-MM'\n * @param {boolean} [props.disableFuture] p. ej. el mes de un reporte que ya cerró\n * @param {boolean} [props.disablePast]\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del DatePicker de MUI X que aquí no tiene nombre (shouldDisableMonth, slots,\n * slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction MonthInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', minMonth, maxMonth, disableFuture = false, disablePast = false,\n disabled = false, inputRef, pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const field = usePickerField({name, kind: 'month', error, label, messages, texts});\n\n return (\n <PickerFrame inputId={field.inputId} label={label} language={field.language}>\n <MonthInputPicker\n {...pickerProps}\n views={MONTH_VIEWS}\n openTo=\"month\"\n format={field.formats.month}\n value={pickerMonthOf(value)}\n minDate={pickerMonthOf(minMonth) ?? undefined}\n maxDate={pickerMonthOf(maxMonth) ?? undefined}\n disableFuture={disableFuture}\n disablePast={disablePast}\n disabled={disabled}\n inputRef={inputRef}\n onChange={(pickedValue) => onChange(monthTextOf(pickedValue))}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf(\n {id: field.inputId, name, onBlur, error: Boolean(field.errorText), helperText: field.errorText ?? helperText},\n pickerProps,\n )}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst MonthInput = withPieceSlots(MonthInputContent, THEME_NAME);\nexport default MonthInput;\n"],"names":[],"mappings":";;;;;;;;AAoBA,MAAM,aAAa;AACnB,MAAM,mBAAmB,UAAU,YAAY,UAAU,UAAU;AAGnE,MAAM,cAAc,CAAC,QAAQ,OAAO;AAuBpC,SAAS,kBAAkB,OAAO;AAC9B,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAU;AAAA,IAAU,gBAAgB;AAAA,IAAO,cAAc;AAAA,IACxH,WAAW;AAAA,IAAO;AAAA,IAAU;AAAA,IAAa;AAAA,IAAU;AAAA,IACnD,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,eAAe,EAAC,MAAM,MAAM,SAAS,OAAO,OAAO,UAAU,OAAM;AAEjF,SACI,oBAAC,eAAY,SAAS,MAAM,SAAS,OAAc,UAAU,MAAM,UAC/D,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MACJ,OAAO;AAAA,MACP,QAAO;AAAA,MACP,QAAQ,MAAM,QAAQ;AAAA,MACtB,OAAO,cAAc,KAAK;AAAA,MAC1B,SAAS,cAAc,QAAQ,KAAK;AAAA,MACpC,SAAS,cAAc,QAAQ,KAAK;AAAA,MACpC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU,CAAC,gBAAgB,SAAS,YAAY,WAAW,CAAC;AAAA,MAC5D,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACP,EAAC,IAAI,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS,GAAG,YAAY,MAAM,aAAa,WAAA;AAAA,QAClG;AAAA,MAAA;AAAA,IACJ;AAAA,EAAA,GAER;AAER;AAGA,MAAM,aAAa,eAAe,mBAAmB,UAAU;"}
|
|
@@ -5,27 +5,32 @@ import PickerFrame, { textFieldSlotPropsOf } from "./PickerFrame.js";
|
|
|
5
5
|
import { DEFAULT_MINUTES_STEP, timeTextOf, pickerTimeOf } from "./pickerValues.js";
|
|
6
6
|
import { usePickerField } from "./usePickerField.js";
|
|
7
7
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
8
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
9
|
+
const THEME_NAME = "CalcitaTimeInput";
|
|
10
|
+
const TimeInputPicker = piecePart(THEME_NAME, "Picker", TimePicker);
|
|
11
|
+
function TimeInputContent(props) {
|
|
12
|
+
const {
|
|
13
|
+
name,
|
|
14
|
+
label,
|
|
15
|
+
value,
|
|
16
|
+
onChange,
|
|
17
|
+
onBlur,
|
|
18
|
+
error,
|
|
19
|
+
helperText = " ",
|
|
20
|
+
minTime,
|
|
21
|
+
maxTime,
|
|
22
|
+
minutesStep = DEFAULT_MINUTES_STEP,
|
|
23
|
+
disabled = false,
|
|
24
|
+
inputRef,
|
|
25
|
+
pickerProps,
|
|
26
|
+
messages,
|
|
27
|
+
texts,
|
|
28
|
+
...unknownProps
|
|
29
|
+
} = usePieceProps(props, THEME_NAME);
|
|
30
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
31
|
+
const field = usePickerField({ name, kind: "time", error, label, messages, texts });
|
|
27
32
|
return /* @__PURE__ */ jsx(PickerFrame, { inputId: field.inputId, label, language: field.language, children: /* @__PURE__ */ jsx(
|
|
28
|
-
|
|
33
|
+
TimeInputPicker,
|
|
29
34
|
{
|
|
30
35
|
...pickerProps,
|
|
31
36
|
format: field.formats.time,
|
|
@@ -49,6 +54,7 @@ function TimeInput({
|
|
|
49
54
|
}
|
|
50
55
|
) });
|
|
51
56
|
}
|
|
57
|
+
const TimeInput = withPieceSlots(TimeInputContent, THEME_NAME);
|
|
52
58
|
export {
|
|
53
59
|
TimeInput as default
|
|
54
60
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeInput.js","sources":["../../src/datePickers/TimeInput.jsx"],"sourcesContent":["// Campo de HORA suelto con el TimePicker de MUI X (@stackcraftmx/calcita/date-pickers), para una hora del día sin fecha:\n// la hora de una cita en un día ya elegido, el horario de una sucursal, «recordar a las 08:00».\n//\n// Viaja 'HH:mm' de 24 horas aunque en pantalla se vea con a.m. y p.m. (el hourCycle del proveedor decide cómo se ve, no\n// cómo viaja). Una hora del día no tiene zona: no se pasa por Date ni por UTC. '' = vacía.\n//\n// Para un instante (fecha y hora de algo que pasa una vez) va DateTimeInput, que sí viaja en UTC.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {TimePicker} from '@mui/x-date-pickers/TimePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {DEFAULT_MINUTES_STEP, pickerTimeOf, timeTextOf} from '@/datePickers/pickerValues.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value 'HH:mm' o ''\n * @param {(value: string) => void} props.onChange recibe 'HH:mm' ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minTime] 'HH:mm'\n * @param {string} [props.maxTime] 'HH:mm'\n * @param {number} [props.minutesStep] de cuánto en cuánto van los minutos; por omisión de uno en uno (30 para citas de\n * media hora)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del TimePicker de MUI X que aquí no tiene nombre (views, shouldDisableTime,\n * slots, slotProps…); value y onChange son del campo\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"TimeInput.js","sources":["../../src/datePickers/TimeInput.jsx"],"sourcesContent":["// Campo de HORA suelto con el TimePicker de MUI X (@stackcraftmx/calcita/date-pickers), para una hora del día sin fecha:\n// la hora de una cita en un día ya elegido, el horario de una sucursal, «recordar a las 08:00».\n//\n// Viaja 'HH:mm' de 24 horas aunque en pantalla se vea con a.m. y p.m. (el hourCycle del proveedor decide cómo se ve, no\n// cómo viaja). Una hora del día no tiene zona: no se pasa por Date ni por UTC. '' = vacía.\n//\n// Para un instante (fecha y hora de algo que pasa una vez) va DateTimeInput, que sí viaja en UTC.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaTimeInput con defaultProps, styleOverrides de\n// sus partes (picker) y slots/slotProps para cambiarlas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {TimePicker} from '@mui/x-date-pickers/TimePicker';\n\nimport PickerFrame, {textFieldSlotPropsOf} from '@/datePickers/PickerFrame.jsx';\nimport {DEFAULT_MINUTES_STEP, pickerTimeOf, timeTextOf} from '@/datePickers/pickerValues.js';\nimport {usePickerField} from '@/datePickers/usePickerField.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaTimeInput)\nconst THEME_NAME = 'CalcitaTimeInput';\nconst TimeInputPicker = piecePart(THEME_NAME, 'Picker', TimePicker);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value 'HH:mm' o ''\n * @param {(value: string) => void} props.onChange recibe 'HH:mm' ('' al borrar o a medias)\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {string} [props.minTime] 'HH:mm'\n * @param {string} [props.maxTime] 'HH:mm'\n * @param {number} [props.minutesStep] de cuánto en cuánto van los minutos; por omisión de uno en uno (30 para citas de\n * media hora)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.pickerProps] lo demás del TimePicker de MUI X que aquí no tiene nombre (views, shouldDisableTime,\n * slots, slotProps…); value y onChange son del campo\n * @param {Record<string, string|object>} [props.messages] el texto del error de este campo, por regla de validación\n * ({required: 'Elija la fecha', min: 'Desde mañana'}): gana al de Calcita y al de los textos\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction TimeInputContent(props) {\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', minTime, maxTime, minutesStep = DEFAULT_MINUTES_STEP, disabled = false, inputRef,\n pickerProps, messages, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const field = usePickerField({name, kind: 'time', error, label, messages, texts});\n\n return (\n <PickerFrame inputId={field.inputId} label={label} language={field.language}>\n <TimeInputPicker\n {...pickerProps}\n format={field.formats.time}\n ampm={field.formats.ampm}\n value={pickerTimeOf(value)}\n minTime={pickerTimeOf(minTime) ?? undefined}\n maxTime={pickerTimeOf(maxTime) ?? undefined}\n disabled={disabled}\n minutesStep={minutesStep}\n timeSteps={{minutes: minutesStep, ...pickerProps?.timeSteps}}\n inputRef={inputRef}\n onChange={(pickedValue) => onChange(timeTextOf(pickedValue))}\n onClose={() => {\n pickerProps?.onClose?.();\n onBlur?.();\n }}\n slotProps={textFieldSlotPropsOf(\n {id: field.inputId, name, onBlur, error: Boolean(field.errorText), helperText: field.errorText ?? helperText},\n pickerProps,\n )}\n />\n </PickerFrame>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst TimeInput = withPieceSlots(TimeInputContent, THEME_NAME);\nexport default TimeInput;\n"],"names":[],"mappings":";;;;;;;;AAqBA,MAAM,aAAa;AACnB,MAAM,kBAAkB,UAAU,YAAY,UAAU,UAAU;AAuBlE,SAAS,iBAAiB,OAAO;AAC7B,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAS;AAAA,IAAS,cAAc;AAAA,IAAsB,WAAW;AAAA,IAAO;AAAA,IACvI;AAAA,IAAa;AAAA,IAAU;AAAA,IACvB,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,QAAQ,eAAe,EAAC,MAAM,MAAM,QAAQ,OAAO,OAAO,UAAU,OAAM;AAEhF,SACI,oBAAC,eAAY,SAAS,MAAM,SAAS,OAAc,UAAU,MAAM,UAC/D,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACI,GAAG;AAAA,MACJ,QAAQ,MAAM,QAAQ;AAAA,MACtB,MAAM,MAAM,QAAQ;AAAA,MACpB,OAAO,aAAa,KAAK;AAAA,MACzB,SAAS,aAAa,OAAO,KAAK;AAAA,MAClC,SAAS,aAAa,OAAO,KAAK;AAAA,MAClC;AAAA,MACA;AAAA,MACA,WAAW,EAAC,SAAS,aAAa,GAAG,aAAa,UAAA;AAAA,MAClD;AAAA,MACA,UAAU,CAAC,gBAAgB,SAAS,WAAW,WAAW,CAAC;AAAA,MAC3D,SAAS,MAAM;AACX,qBAAa,UAAA;AACb,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACP,EAAC,IAAI,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS,GAAG,YAAY,MAAM,aAAa,WAAA;AAAA,QAClG;AAAA,MAAA;AAAA,IACJ;AAAA,EAAA,GAER;AAER;AAGA,MAAM,YAAY,eAAe,kBAAkB,UAAU;"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useId } from "react";
|
|
3
3
|
import { displayFormatsOf } from "./pickerValues.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useValidationText } from "../forms/useValidationText.js";
|
|
5
5
|
import { useToolkitSettings } from "../toolkit/toolkitContext.js";
|
|
6
6
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
7
|
-
function usePickerField({ name, kind, error, texts }) {
|
|
8
|
-
const {
|
|
7
|
+
function usePickerField({ name, kind, error, label, messages, texts }) {
|
|
8
|
+
const { language } = useToolkitTranslation("common", texts);
|
|
9
|
+
const validationTextOf = useValidationText({ name, label, messages });
|
|
9
10
|
const { timeZone, hourCycle } = useToolkitSettings();
|
|
10
11
|
const generatedId = useId();
|
|
11
12
|
return {
|
|
12
13
|
language,
|
|
13
14
|
timeZone,
|
|
14
15
|
inputId: `${name ?? kind}-${generatedId}`,
|
|
15
|
-
errorText:
|
|
16
|
+
errorText: validationTextOf(error),
|
|
16
17
|
formats: displayFormatsOf(language, hourCycle)
|
|
17
18
|
};
|
|
18
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePickerField.js","sources":["../../src/datePickers/usePickerField.js"],"sourcesContent":["// Lo que necesita cada campo de fecha antes de pintarse: los textos (con los de la pantalla encima), el id que une la\n// etiqueta con el campo, el error ya traducido y cómo se ve en el idioma y el hourCycle del proveedor más cercano.\n//\n// Hook genérico: function (regla de la casa).\nimport {useId} from 'react';\n\nimport {displayFormatsOf} from '@/datePickers/pickerValues.js';\nimport {
|
|
1
|
+
{"version":3,"file":"usePickerField.js","sources":["../../src/datePickers/usePickerField.js"],"sourcesContent":["// Lo que necesita cada campo de fecha antes de pintarse: los textos (con los de la pantalla encima), el id que une la\n// etiqueta con el campo, el error ya traducido y cómo se ve en el idioma y el hourCycle del proveedor más cercano.\n//\n// Hook genérico: function (regla de la casa).\nimport {useId} from 'react';\n\nimport {displayFormatsOf} from '@/datePickers/pickerValues.js';\nimport {useValidationText} from '@/forms/useValidationText.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/**\n * @param {object} field\n * @param {string} [field.name]\n * @param {string} field.kind de qué campo es (date, dateTime, time, month): da el principio del id si no hay name\n * @param {string|{key: string, values?: object}} [field.error]\n * @param {string} [field.label] el nombre del campo, para los textos de error que lo dicen\n * @param {Record<string, string|object>} [field.messages] el texto del error por regla de validación (gana al de Calcita)\n * @param {object} [field.texts] los textos que cambian, con la forma de `common`\n * @returns {{language: string, timeZone: string, inputId: string, errorText: string|undefined, formats: ReturnType<typeof displayFormatsOf>}}\n */\nexport function usePickerField({name, kind, error, label, messages, texts}) {\n const {language} = useToolkitTranslation('common', texts);\n const validationTextOf = useValidationText({name, label, messages});\n const {timeZone, hourCycle} = useToolkitSettings();\n const generatedId = useId();\n\n return {\n language,\n timeZone,\n inputId: `${name ?? kind}-${generatedId}`,\n errorText: validationTextOf(error),\n formats: displayFormatsOf(language, hourCycle),\n };\n}\n"],"names":[],"mappings":";;;;;;AAqBO,SAAS,eAAe,EAAC,MAAM,MAAM,OAAO,OAAO,UAAU,MAAK,GAAG;AACxE,QAAM,EAAC,SAAQ,IAAI,sBAAsB,UAAU,KAAK;AACxD,QAAM,mBAAmB,kBAAkB,EAAC,MAAM,OAAO,SAAQ,CAAC;AAClE,QAAM,EAAC,UAAU,UAAS,IAAI,mBAAkB;AAChD,QAAM,cAAc,MAAK;AAEzB,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA,SAAS,GAAG,QAAQ,IAAI,IAAI,WAAW;AAAA,IACvC,WAAW,iBAAiB,KAAK;AAAA,IACjC,SAAS,iBAAiB,UAAU,SAAS;AAAA,EACrD;AACA;"}
|
|
@@ -27,7 +27,7 @@ import { DOWNLOAD_STATUS } from "./downloadsMachine.js";
|
|
|
27
27
|
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
28
28
|
import { isGlassTheme, glassStylesOf } from "../theme/glassSurface.js";
|
|
29
29
|
import ComponentTexts from "../toolkit/ComponentTexts.js";
|
|
30
|
-
import { piecePart } from "../theme/themePieces.js";
|
|
30
|
+
import { piecePart, withPieceSlots } from "../theme/themePieces.js";
|
|
31
31
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
32
32
|
const THEME_NAME = "CalcitaDownloadManager";
|
|
33
33
|
const DownloadsDoneIcon = piecePart(THEME_NAME, "DoneIcon", CheckCircleIcon);
|
|
@@ -143,7 +143,7 @@ function DownloadRow({ download }) {
|
|
|
143
143
|
] })
|
|
144
144
|
);
|
|
145
145
|
}
|
|
146
|
-
function
|
|
146
|
+
function DownloadManagerContent({ texts, ...unknownProps } = {}) {
|
|
147
147
|
warnUnknownProps("DownloadManager", unknownProps);
|
|
148
148
|
const { t } = useToolkitTranslation("common", texts);
|
|
149
149
|
const downloads = useSelector(downloadsActor, (snapshot) => snapshot.context.downloads);
|
|
@@ -207,6 +207,7 @@ function DownloadManager({ texts, ...unknownProps } = {}) {
|
|
|
207
207
|
}
|
|
208
208
|
) }) });
|
|
209
209
|
}
|
|
210
|
+
const DownloadManager = withPieceSlots(DownloadManagerContent, THEME_NAME);
|
|
210
211
|
export {
|
|
211
212
|
DownloadManager as default
|
|
212
213
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadManager.js","sources":["../../src/downloads/DownloadManager.jsx"],"sourcesContent":["// Panel de descargas al estilo de Google Drive: se pega abajo a la derecha SOLO mientras hay descargas, con una barra que\n// resume («Descargando 2 archivos…», «3 descargas listas») y la lista con el ícono del tipo de archivo y un anillo de\n// avance que termina en ✓. Con MUI y XState, sin Redux.\n//\n// Qué hace para no hacer ruido:\n// - no hay botón fijo: sin descargas no se pinta nada;\n// - se pliega a la barra con la flecha y se vuelve a abrir cuando entra otra descarga;\n// - cuando todo terminó bien se va solo a los AUTO_DISMISS_MILLISECONDS; si algo falló o se canceló se queda hasta que\n// la persona lo cierra, para que vea el motivo;\n// - la X solo aparece cuando ya nada descarga (cerrar a media descarga dejaría el archivo sin aviso de que terminó).\n//\n// Se monta UNA sola vez en la raíz de la aplicación. Lee la lista de downloadsActor; las descargas las arranca startDownload desde cada\n// pantalla. Los avisos (sonner) salen abajo a la izquierda, así que el panel va a la derecha.\n// Con el vidrio del tema (createToolkitTheme({glass: true})) el panel es de vidrio: flota sobre la pantalla.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDownloadManager con defaultProps y\n// styleOverrides de sus partes: doneIcon, failedIcon, canceledIcon, cancelButton, progress, cancelIcon, item, itemText,\n// itemStatus, itemActions, panel, header, title, list.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {Fragment, useEffect, useRef, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useSelector} from '@xstate/react';\nimport Box from '@mui/material/Box';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Collapse from '@mui/material/Collapse';\nimport Divider from '@mui/material/Divider';\nimport IconButton from '@mui/material/IconButton';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport Paper from '@mui/material/Paper';\nimport Slide from '@mui/material/Slide';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport BlockOutlinedIcon from '@mui/icons-material/BlockOutlined';\nimport CheckCircleIcon from '@mui/icons-material/CheckCircle';\nimport CloseIcon from '@mui/icons-material/Close';\nimport ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';\nimport ExpandLessIcon from '@mui/icons-material/ExpandLess';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {cancelDownload, clearFinishedDownloads, countActiveDownloads, downloadsActor} from '@/downloads/downloadQueue.js';\nimport {UNKNOWN_FILE_TYPE, useFileTypes} from '@/downloads/fileTypes.js';\nimport {DOWNLOAD_STATUS} from '@/downloads/downloadsMachine.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDownloadManager)\nconst THEME_NAME = 'CalcitaDownloadManager';\nconst DownloadsDoneIcon = piecePart(THEME_NAME, 'DoneIcon', CheckCircleIcon);\nconst DownloadsFailedIcon = piecePart(THEME_NAME, 'FailedIcon', ErrorOutlineIcon);\nconst DownloadsCanceledIcon = piecePart(THEME_NAME, 'CanceledIcon', BlockOutlinedIcon);\nconst DownloadsCancelButton = piecePart(THEME_NAME, 'CancelButton', IconButton);\nconst DownloadsProgress = piecePart(THEME_NAME, 'Progress', CircularProgress);\nconst DownloadsCancelIcon = piecePart(THEME_NAME, 'CancelIcon', CloseIcon);\nconst DownloadsItem = piecePart(THEME_NAME, 'Item', ListItem);\nconst DownloadsItemText = piecePart(THEME_NAME, 'ItemText', Box);\nconst DownloadsItemStatus = piecePart(THEME_NAME, 'ItemStatus', Typography);\nconst DownloadsItemActions = piecePart(THEME_NAME, 'ItemActions', Box);\nconst DownloadsPanel = piecePart(THEME_NAME, 'Panel', Paper);\nconst DownloadsHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst DownloadsTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst DownloadsList = piecePart(THEME_NAME, 'List', List);\n\n// cuánto se queda el panel después de que TODAS las descargas terminaron bien\nconst AUTO_DISMISS_MILLISECONDS = 5000;\n\n// separación del borde derecho y ancho del panel (en el teléfono ocupa el ancho menos los márgenes)\nconst SCREEN_EDGE_SPACING = 3;\nconst PANEL_WIDTH_PIXELS = 360;\nconst LIST_MAX_HEIGHT = '40vh';\nconst PROGRESS_RING_PIXELS = 22;\n\n/**\n * Tipo de archivo de una descarga: el que dijo quien la arrancó o, al terminar, la extensión del nombre. Los tipos son los de\n * la casa y los del proyecto (ToolkitProvider fileTypes).\n *\n * @param {object} download renglón de la lista (downloadsMachine)\n * @param {Record<string, {Icon: import('react').ElementType, color?: string}>} fileTypes\n * @returns {{Icon: import('react').ElementType, color: string}}\n */\nfunction fileTypeAppearanceOf(download, fileTypes) {\n const extension = download.fileName?.split('.').pop()?.toLowerCase();\n const fileType = fileTypes[download.format ?? extension];\n return fileType ? {Icon: fileType.Icon ?? UNKNOWN_FILE_TYPE.Icon, color: fileType.color ?? UNKNOWN_FILE_TYPE.color} : UNKNOWN_FILE_TYPE;\n}\n\n/**\n * Segunda línea del renglón: cuánto lleva, cuánto pesó o por qué no se pudo.\n *\n * @param {object} download\n * @param {Function} t traductor de common\n * @param {(bytes: number) => string} formatFileSize el de useFormatters (el idioma del proveedor más cercano)\n * @returns {string}\n */\nfunction detailTextOf(download, t, formatFileSize) {\n if (download.status === DOWNLOAD_STATUS.completed) {\n return formatFileSize(download.loadedBytes);\n }\n if (download.status === DOWNLOAD_STATUS.failed) {\n return download.errorMessage ?? t('downloads.failed');\n }\n if (download.status === DOWNLOAD_STATUS.canceled) {\n return t('downloads.canceled');\n }\n if (download.loadedBytes === 0) {\n return t('downloads.preparing');\n }\n // un archivo que se arma al vuelo (el log) no dice su tamaño: se enseña lo que ya bajó\n return download.totalBytes\n ? t('downloads.progressOf', {loaded: formatFileSize(download.loadedBytes), total: formatFileSize(download.totalBytes)})\n : t('downloads.progressLoaded', {loaded: formatFileSize(download.loadedBytes)});\n}\n\n/**\n * Resumen de la barra, como en Drive: lo que está pasando ahora o cómo terminó.\n *\n * @param {Array<object>} downloads\n * @param {Function} t traductor de common\n * @returns {string}\n */\nfunction summaryTextOf(downloads, t) {\n const activeCount = countActiveDownloads(downloads);\n if (activeCount > 0) {\n return t('downloads.summaryDownloading', {count: activeCount});\n }\n const failedCount = downloads.filter((download) => download.status === DOWNLOAD_STATUS.failed).length;\n if (failedCount > 0) {\n return t('downloads.summaryFailed', {count: failedCount});\n }\n const completedCount = downloads.filter((download) => download.status === DOWNLOAD_STATUS.completed).length;\n return t('downloads.summaryCompleted', {count: completedCount});\n}\n\n/**\n * Lo que va a la derecha del renglón: el anillo de avance con su botón para cancelar, o cómo terminó.\n *\n * @param {{download: object, displayName: string}} props\n */\nfunction DownloadStatusIndicator({download, displayName}) {\n const {t} = useToolkitTranslation('common');\n\n if (download.status === DOWNLOAD_STATUS.completed) {\n return <DownloadsDoneIcon ownSx={{color: 'success.main'}} aria-label={t('downloads.completedOf', {name: displayName})} />;\n }\n if (download.status === DOWNLOAD_STATUS.failed) {\n return <DownloadsFailedIcon ownSx={{color: 'error.main'}} aria-label={t('downloads.failedOf', {name: displayName})} />;\n }\n if (download.status === DOWNLOAD_STATUS.canceled) {\n return <DownloadsCanceledIcon ownSx={{color: 'text.disabled'}} aria-label={t('downloads.canceledOf', {name: displayName})} />;\n }\n\n const hasKnownSize = Boolean(download.totalBytes);\n // el anillo y, encima, la X para cancelar: como en Drive, un solo lugar para ver y detener\n return (\n <Tooltip title={t('downloads.cancel')}>\n <DownloadsCancelButton size=\"small\" aria-label={t('downloads.cancelOf', {name: displayName})} onClick={() => cancelDownload(download.id)}\n ownSx={{position: 'relative', width: PROGRESS_RING_PIXELS + 10, height: PROGRESS_RING_PIXELS + 10}}>\n <DownloadsProgress\n size={PROGRESS_RING_PIXELS}\n thickness={4}\n variant={hasKnownSize ? 'determinate' : 'indeterminate'}\n value={hasKnownSize ? Math.min(100, (download.loadedBytes * 100) / download.totalBytes) : undefined}\n ownSx={{position: 'absolute'}}\n />\n <DownloadsCancelIcon ownSx={{fontSize: 12}} />\n </DownloadsCancelButton>\n </Tooltip>\n );\n}\n\n/**\n * Un renglón: ícono del tipo de archivo, nombre, detalle y estatus.\n *\n * @param {{download: object}} props\n */\nfunction DownloadRow({download}) {\n const {t} = useToolkitTranslation('common');\n const {formatFileSize} = useFormatters();\n const {Icon, color} = fileTypeAppearanceOf(download, useFileTypes());\n const displayName = download.fileName ?? download.label;\n const isFailed = download.status === DOWNLOAD_STATUS.failed;\n\n return (\n // minWidth 0: sin él un nombre largo empuja el renglón y la lista enseña una barra horizontal\n <DownloadsItem disableGutters ownSx={{gap: 1.5, px: 2, py: 1, minWidth: 0}}>\n <Icon fontSize=\"small\" sx={{color, flexShrink: 0}} />\n <DownloadsItemText ownSx={{minWidth: 0, flex: 1}}>\n <Tooltip title={displayName}>\n <Typography variant=\"body2\" noWrap>{displayName}</Typography>\n </Tooltip>\n <DownloadsItemStatus variant=\"caption\" component=\"p\" noWrap={!isFailed}\n ownSx={{color: isFailed ? 'error.main' : 'text.secondary'}}>\n {detailTextOf(download, t, formatFileSize)}\n </DownloadsItemStatus>\n </DownloadsItemText>\n <DownloadsItemActions ownSx={{flexShrink: 0, display: 'flex', alignItems: 'center'}}>\n <DownloadStatusIndicator download={download} displayName={displayName} />\n </DownloadsItemActions>\n </DownloadsItem>\n );\n}\n\n/**\n * @param {object} [props]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function DownloadManager({texts, ...unknownProps} = {}) {\n warnUnknownProps('DownloadManager', unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const downloads = useSelector(downloadsActor, (snapshot) => snapshot.context.downloads);\n const [isExpanded, setIsExpanded] = useState(true);\n const previousCountRef = useRef(downloads.length);\n\n const activeCount = countActiveDownloads(downloads);\n const allSucceeded = downloads.length > 0 && downloads.every((download) => download.status === DOWNLOAD_STATUS.completed);\n\n // una descarga nueva vuelve a desplegar el panel aunque se haya plegado\n useEffect(() => {\n if (downloads.length > previousCountRef.current) {\n setIsExpanded(true);\n }\n previousCountRef.current = downloads.length;\n }, [downloads.length]);\n\n // todo terminó bien: el panel se va solo (con un error o una cancelada se queda para que se lea)\n useEffect(() => {\n if (!allSucceeded) {\n return undefined;\n }\n const dismissTimer = setTimeout(clearFinishedDownloads, AUTO_DISMISS_MILLISECONDS);\n return () => clearTimeout(dismissTimer);\n }, [allSucceeded, downloads.length]);\n\n return (\n <ComponentTexts namespace=\"common\" texts={texts}>\n <Slide direction=\"up\" in={downloads.length > 0} mountOnEnter unmountOnExit>\n <DownloadsPanel\n elevation={8}\n role=\"region\"\n aria-label={t('downloads.title')}\n ownSx={[{\n position: 'fixed',\n right: (theme) => theme.spacing(SCREEN_EDGE_SPACING),\n bottom: 0,\n width: (theme) => `min(${PANEL_WIDTH_PIXELS}px, calc(100vw - ${theme.spacing(SCREEN_EDGE_SPACING * 2)}))`,\n // pegado al borde de abajo como en Drive: solo se redondean las esquinas de arriba\n borderBottomLeftRadius: 0,\n borderBottomRightRadius: 0,\n overflow: 'hidden',\n zIndex: (theme) => theme.zIndex.snackbar,\n }, (theme) => (isGlassTheme(theme) ? glassStylesOf(theme) : {})]}\n >\n <DownloadsHeader direction=\"row\" ownSx={{alignItems: 'center', gap: 0.5, pl: 2, pr: 1, py: 1, bgcolor: 'action.hover'}}>\n <DownloadsTitle variant=\"subtitle2\" component=\"h2\" noWrap ownSx={{flex: 1}} aria-live=\"polite\">\n {summaryTextOf(downloads, t)}\n </DownloadsTitle>\n <Tooltip title={isExpanded ? t('downloads.collapse') : t('downloads.expand')}>\n <IconButton size=\"small\" aria-expanded={isExpanded}\n aria-label={isExpanded ? t('downloads.collapse') : t('downloads.expand')}\n onClick={() => setIsExpanded((wasExpanded) => !wasExpanded)}>\n {isExpanded ? <ExpandMoreIcon fontSize=\"small\" /> : <ExpandLessIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n {/* cerrar a media descarga dejaría el archivo sin aviso de que terminó: la X sale cuando ya nada baja */}\n {activeCount === 0 && (\n <Tooltip title={t('downloads.close')}>\n <IconButton size=\"small\" aria-label={t('downloads.close')} onClick={clearFinishedDownloads}>\n <CloseIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </DownloadsHeader>\n <Collapse in={isExpanded}>\n <Divider />\n {/* OJO: overflowY auto también prende el scroll horizontal ante 1 px de más; se apaga explícito */}\n <DownloadsList dense disablePadding ownSx={{maxHeight: LIST_MAX_HEIGHT, overflowY: 'auto', overflowX: 'hidden'}}>\n {downloads.map((download, index) => (\n <Fragment key={download.id}>\n {index > 0 && <Divider component=\"li\" />}\n <DownloadRow download={download} />\n </Fragment>\n ))}\n </DownloadsList>\n </Collapse>\n </DownloadsPanel>\n </Slide>\n </ComponentTexts>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DownloadManager.js","sources":["../../src/downloads/DownloadManager.jsx"],"sourcesContent":["// Panel de descargas al estilo de Google Drive: se pega abajo a la derecha SOLO mientras hay descargas, con una barra que\n// resume («Descargando 2 archivos…», «3 descargas listas») y la lista con el ícono del tipo de archivo y un anillo de\n// avance que termina en ✓. Con MUI y XState, sin Redux.\n//\n// Qué hace para no hacer ruido:\n// - no hay botón fijo: sin descargas no se pinta nada;\n// - se pliega a la barra con la flecha y se vuelve a abrir cuando entra otra descarga;\n// - cuando todo terminó bien se va solo a los AUTO_DISMISS_MILLISECONDS; si algo falló o se canceló se queda hasta que\n// la persona lo cierra, para que vea el motivo;\n// - la X solo aparece cuando ya nada descarga (cerrar a media descarga dejaría el archivo sin aviso de que terminó).\n//\n// Se monta UNA sola vez en la raíz de la aplicación. Lee la lista de downloadsActor; las descargas las arranca startDownload desde cada\n// pantalla. Los avisos (sonner) salen abajo a la izquierda, así que el panel va a la derecha.\n// Con el vidrio del tema (createToolkitTheme({glass: true})) el panel es de vidrio: flota sobre la pantalla.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDownloadManager con defaultProps y\n// styleOverrides de sus partes: doneIcon, failedIcon, canceledIcon, cancelButton, progress, cancelIcon, item, itemText,\n// itemStatus, itemActions, panel, header, title, list.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {Fragment, useEffect, useRef, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useSelector} from '@xstate/react';\nimport Box from '@mui/material/Box';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Collapse from '@mui/material/Collapse';\nimport Divider from '@mui/material/Divider';\nimport IconButton from '@mui/material/IconButton';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport Paper from '@mui/material/Paper';\nimport Slide from '@mui/material/Slide';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport BlockOutlinedIcon from '@mui/icons-material/BlockOutlined';\nimport CheckCircleIcon from '@mui/icons-material/CheckCircle';\nimport CloseIcon from '@mui/icons-material/Close';\nimport ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';\nimport ExpandLessIcon from '@mui/icons-material/ExpandLess';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {cancelDownload, clearFinishedDownloads, countActiveDownloads, downloadsActor} from '@/downloads/downloadQueue.js';\nimport {UNKNOWN_FILE_TYPE, useFileTypes} from '@/downloads/fileTypes.js';\nimport {DOWNLOAD_STATUS} from '@/downloads/downloadsMachine.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport {piecePart, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDownloadManager)\nconst THEME_NAME = 'CalcitaDownloadManager';\nconst DownloadsDoneIcon = piecePart(THEME_NAME, 'DoneIcon', CheckCircleIcon);\nconst DownloadsFailedIcon = piecePart(THEME_NAME, 'FailedIcon', ErrorOutlineIcon);\nconst DownloadsCanceledIcon = piecePart(THEME_NAME, 'CanceledIcon', BlockOutlinedIcon);\nconst DownloadsCancelButton = piecePart(THEME_NAME, 'CancelButton', IconButton);\nconst DownloadsProgress = piecePart(THEME_NAME, 'Progress', CircularProgress);\nconst DownloadsCancelIcon = piecePart(THEME_NAME, 'CancelIcon', CloseIcon);\nconst DownloadsItem = piecePart(THEME_NAME, 'Item', ListItem);\nconst DownloadsItemText = piecePart(THEME_NAME, 'ItemText', Box);\nconst DownloadsItemStatus = piecePart(THEME_NAME, 'ItemStatus', Typography);\nconst DownloadsItemActions = piecePart(THEME_NAME, 'ItemActions', Box);\nconst DownloadsPanel = piecePart(THEME_NAME, 'Panel', Paper);\nconst DownloadsHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst DownloadsTitle = piecePart(THEME_NAME, 'Title', Typography);\nconst DownloadsList = piecePart(THEME_NAME, 'List', List);\n\n// cuánto se queda el panel después de que TODAS las descargas terminaron bien\nconst AUTO_DISMISS_MILLISECONDS = 5000;\n\n// separación del borde derecho y ancho del panel (en el teléfono ocupa el ancho menos los márgenes)\nconst SCREEN_EDGE_SPACING = 3;\nconst PANEL_WIDTH_PIXELS = 360;\nconst LIST_MAX_HEIGHT = '40vh';\nconst PROGRESS_RING_PIXELS = 22;\n\n/**\n * Tipo de archivo de una descarga: el que dijo quien la arrancó o, al terminar, la extensión del nombre. Los tipos son los de\n * la casa y los del proyecto (ToolkitProvider fileTypes).\n *\n * @param {object} download renglón de la lista (downloadsMachine)\n * @param {Record<string, {Icon: import('react').ElementType, color?: string}>} fileTypes\n * @returns {{Icon: import('react').ElementType, color: string}}\n */\nfunction fileTypeAppearanceOf(download, fileTypes) {\n const extension = download.fileName?.split('.').pop()?.toLowerCase();\n const fileType = fileTypes[download.format ?? extension];\n return fileType ? {Icon: fileType.Icon ?? UNKNOWN_FILE_TYPE.Icon, color: fileType.color ?? UNKNOWN_FILE_TYPE.color} : UNKNOWN_FILE_TYPE;\n}\n\n/**\n * Segunda línea del renglón: cuánto lleva, cuánto pesó o por qué no se pudo.\n *\n * @param {object} download\n * @param {Function} t traductor de common\n * @param {(bytes: number) => string} formatFileSize el de useFormatters (el idioma del proveedor más cercano)\n * @returns {string}\n */\nfunction detailTextOf(download, t, formatFileSize) {\n if (download.status === DOWNLOAD_STATUS.completed) {\n return formatFileSize(download.loadedBytes);\n }\n if (download.status === DOWNLOAD_STATUS.failed) {\n return download.errorMessage ?? t('downloads.failed');\n }\n if (download.status === DOWNLOAD_STATUS.canceled) {\n return t('downloads.canceled');\n }\n if (download.loadedBytes === 0) {\n return t('downloads.preparing');\n }\n // un archivo que se arma al vuelo (el log) no dice su tamaño: se enseña lo que ya bajó\n return download.totalBytes\n ? t('downloads.progressOf', {loaded: formatFileSize(download.loadedBytes), total: formatFileSize(download.totalBytes)})\n : t('downloads.progressLoaded', {loaded: formatFileSize(download.loadedBytes)});\n}\n\n/**\n * Resumen de la barra, como en Drive: lo que está pasando ahora o cómo terminó.\n *\n * @param {Array<object>} downloads\n * @param {Function} t traductor de common\n * @returns {string}\n */\nfunction summaryTextOf(downloads, t) {\n const activeCount = countActiveDownloads(downloads);\n if (activeCount > 0) {\n return t('downloads.summaryDownloading', {count: activeCount});\n }\n const failedCount = downloads.filter((download) => download.status === DOWNLOAD_STATUS.failed).length;\n if (failedCount > 0) {\n return t('downloads.summaryFailed', {count: failedCount});\n }\n const completedCount = downloads.filter((download) => download.status === DOWNLOAD_STATUS.completed).length;\n return t('downloads.summaryCompleted', {count: completedCount});\n}\n\n/**\n * Lo que va a la derecha del renglón: el anillo de avance con su botón para cancelar, o cómo terminó.\n *\n * @param {{download: object, displayName: string}} props\n */\nfunction DownloadStatusIndicator({download, displayName}) {\n const {t} = useToolkitTranslation('common');\n\n if (download.status === DOWNLOAD_STATUS.completed) {\n return <DownloadsDoneIcon ownSx={{color: 'success.main'}} aria-label={t('downloads.completedOf', {name: displayName})} />;\n }\n if (download.status === DOWNLOAD_STATUS.failed) {\n return <DownloadsFailedIcon ownSx={{color: 'error.main'}} aria-label={t('downloads.failedOf', {name: displayName})} />;\n }\n if (download.status === DOWNLOAD_STATUS.canceled) {\n return <DownloadsCanceledIcon ownSx={{color: 'text.disabled'}} aria-label={t('downloads.canceledOf', {name: displayName})} />;\n }\n\n const hasKnownSize = Boolean(download.totalBytes);\n // el anillo y, encima, la X para cancelar: como en Drive, un solo lugar para ver y detener\n return (\n <Tooltip title={t('downloads.cancel')}>\n <DownloadsCancelButton size=\"small\" aria-label={t('downloads.cancelOf', {name: displayName})} onClick={() => cancelDownload(download.id)}\n ownSx={{position: 'relative', width: PROGRESS_RING_PIXELS + 10, height: PROGRESS_RING_PIXELS + 10}}>\n <DownloadsProgress\n size={PROGRESS_RING_PIXELS}\n thickness={4}\n variant={hasKnownSize ? 'determinate' : 'indeterminate'}\n value={hasKnownSize ? Math.min(100, (download.loadedBytes * 100) / download.totalBytes) : undefined}\n ownSx={{position: 'absolute'}}\n />\n <DownloadsCancelIcon ownSx={{fontSize: 12}} />\n </DownloadsCancelButton>\n </Tooltip>\n );\n}\n\n/**\n * Un renglón: ícono del tipo de archivo, nombre, detalle y estatus.\n *\n * @param {{download: object}} props\n */\nfunction DownloadRow({download}) {\n const {t} = useToolkitTranslation('common');\n const {formatFileSize} = useFormatters();\n const {Icon, color} = fileTypeAppearanceOf(download, useFileTypes());\n const displayName = download.fileName ?? download.label;\n const isFailed = download.status === DOWNLOAD_STATUS.failed;\n\n return (\n // minWidth 0: sin él un nombre largo empuja el renglón y la lista enseña una barra horizontal\n <DownloadsItem disableGutters ownSx={{gap: 1.5, px: 2, py: 1, minWidth: 0}}>\n <Icon fontSize=\"small\" sx={{color, flexShrink: 0}} />\n <DownloadsItemText ownSx={{minWidth: 0, flex: 1}}>\n <Tooltip title={displayName}>\n <Typography variant=\"body2\" noWrap>{displayName}</Typography>\n </Tooltip>\n <DownloadsItemStatus variant=\"caption\" component=\"p\" noWrap={!isFailed}\n ownSx={{color: isFailed ? 'error.main' : 'text.secondary'}}>\n {detailTextOf(download, t, formatFileSize)}\n </DownloadsItemStatus>\n </DownloadsItemText>\n <DownloadsItemActions ownSx={{flexShrink: 0, display: 'flex', alignItems: 'center'}}>\n <DownloadStatusIndicator download={download} displayName={displayName} />\n </DownloadsItemActions>\n </DownloadsItem>\n );\n}\n\n/**\n * @param {object} [props]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction DownloadManagerContent({texts, ...unknownProps} = {}) {\n warnUnknownProps('DownloadManager', unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const downloads = useSelector(downloadsActor, (snapshot) => snapshot.context.downloads);\n const [isExpanded, setIsExpanded] = useState(true);\n const previousCountRef = useRef(downloads.length);\n\n const activeCount = countActiveDownloads(downloads);\n const allSucceeded = downloads.length > 0 && downloads.every((download) => download.status === DOWNLOAD_STATUS.completed);\n\n // una descarga nueva vuelve a desplegar el panel aunque se haya plegado\n useEffect(() => {\n if (downloads.length > previousCountRef.current) {\n setIsExpanded(true);\n }\n previousCountRef.current = downloads.length;\n }, [downloads.length]);\n\n // todo terminó bien: el panel se va solo (con un error o una cancelada se queda para que se lea)\n useEffect(() => {\n if (!allSucceeded) {\n return undefined;\n }\n const dismissTimer = setTimeout(clearFinishedDownloads, AUTO_DISMISS_MILLISECONDS);\n return () => clearTimeout(dismissTimer);\n }, [allSucceeded, downloads.length]);\n\n return (\n <ComponentTexts namespace=\"common\" texts={texts}>\n <Slide direction=\"up\" in={downloads.length > 0} mountOnEnter unmountOnExit>\n <DownloadsPanel\n elevation={8}\n role=\"region\"\n aria-label={t('downloads.title')}\n ownSx={[{\n position: 'fixed',\n right: (theme) => theme.spacing(SCREEN_EDGE_SPACING),\n bottom: 0,\n width: (theme) => `min(${PANEL_WIDTH_PIXELS}px, calc(100vw - ${theme.spacing(SCREEN_EDGE_SPACING * 2)}))`,\n // pegado al borde de abajo como en Drive: solo se redondean las esquinas de arriba\n borderBottomLeftRadius: 0,\n borderBottomRightRadius: 0,\n overflow: 'hidden',\n zIndex: (theme) => theme.zIndex.snackbar,\n }, (theme) => (isGlassTheme(theme) ? glassStylesOf(theme) : {})]}\n >\n <DownloadsHeader direction=\"row\" ownSx={{alignItems: 'center', gap: 0.5, pl: 2, pr: 1, py: 1, bgcolor: 'action.hover'}}>\n <DownloadsTitle variant=\"subtitle2\" component=\"h2\" noWrap ownSx={{flex: 1}} aria-live=\"polite\">\n {summaryTextOf(downloads, t)}\n </DownloadsTitle>\n <Tooltip title={isExpanded ? t('downloads.collapse') : t('downloads.expand')}>\n <IconButton size=\"small\" aria-expanded={isExpanded}\n aria-label={isExpanded ? t('downloads.collapse') : t('downloads.expand')}\n onClick={() => setIsExpanded((wasExpanded) => !wasExpanded)}>\n {isExpanded ? <ExpandMoreIcon fontSize=\"small\" /> : <ExpandLessIcon fontSize=\"small\" />}\n </IconButton>\n </Tooltip>\n {/* cerrar a media descarga dejaría el archivo sin aviso de que terminó: la X sale cuando ya nada baja */}\n {activeCount === 0 && (\n <Tooltip title={t('downloads.close')}>\n <IconButton size=\"small\" aria-label={t('downloads.close')} onClick={clearFinishedDownloads}>\n <CloseIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </DownloadsHeader>\n <Collapse in={isExpanded}>\n <Divider />\n {/* OJO: overflowY auto también prende el scroll horizontal ante 1 px de más; se apaga explícito */}\n <DownloadsList dense disablePadding ownSx={{maxHeight: LIST_MAX_HEIGHT, overflowY: 'auto', overflowX: 'hidden'}}>\n {downloads.map((download, index) => (\n <Fragment key={download.id}>\n {index > 0 && <Divider component=\"li\" />}\n <DownloadRow download={download} />\n </Fragment>\n ))}\n </DownloadsList>\n </Collapse>\n </DownloadsPanel>\n </Slide>\n </ComponentTexts>\n );\n}\n\nconst DownloadManager = withPieceSlots(DownloadManagerContent, THEME_NAME);\nexport default DownloadManager;\n"],"names":[],"mappings":"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"}
|
|
@@ -10,12 +10,12 @@ import DialogActions from "@mui/material/DialogActions";
|
|
|
10
10
|
import DialogContent from "@mui/material/DialogContent";
|
|
11
11
|
import DialogContentText from "@mui/material/DialogContentText";
|
|
12
12
|
import DialogTitle from "@mui/material/DialogTitle";
|
|
13
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
13
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
14
14
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
15
15
|
const THEME_NAME = "CalcitaConfirmDialog";
|
|
16
16
|
const ConfirmProblem = piecePart(THEME_NAME, "Problem", Alert);
|
|
17
17
|
const ConfirmActions = piecePart(THEME_NAME, "Actions", DialogActions);
|
|
18
|
-
function
|
|
18
|
+
function ConfirmDialogContent(props) {
|
|
19
19
|
const {
|
|
20
20
|
open,
|
|
21
21
|
title,
|
|
@@ -75,6 +75,7 @@ function ConfirmDialog(props) {
|
|
|
75
75
|
}
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
|
+
const ConfirmDialog = withPieceSlots(ConfirmDialogContent, THEME_NAME);
|
|
78
79
|
export {
|
|
79
80
|
ConfirmDialog as default
|
|
80
81
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.js","sources":["../../src/feedback/ConfirmDialog.jsx"],"sourcesContent":["// Diálogo de confirmación: toda acción que cambia algo, manda un correo o descarga un archivo pasa por aquí antes de\n// ejecutarse (regla de tablas). Dice en lenguaje ciudadano QUÉ va a pasar y a QUÉ renglón («¿Dar de baja a Ana López?\n// Ya no podrá entrar; se puede reactivar después»).\n//\n// Recibe el estado de la petición por props (pending, error) en vez de hacerla él: core no conoce el API. Lo normal es\n// alimentarlo con useConfirmAction (máquina confirmActionMachine), que ya garantiza que cancelar no ejecuta y que un\n// doble clic no manda dos peticiones. Mientras la petición está en curso no se puede cerrar ni volver a confirmar.\n//\n// children: lo que el diálogo necesita pedir antes de confirmar (p. ej. el motivo de una baja), debajo del texto.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaConfirmDialog con defaultProps y\n// styleOverrides de sus partes: problem, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\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.CalcitaConfirmDialog)\nconst THEME_NAME = 'CalcitaConfirmDialog';\nconst ConfirmProblem = piecePart(THEME_NAME, 'Problem', Alert);\nconst ConfirmActions = piecePart(THEME_NAME, 'Actions', DialogActions);\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {string} props.title texto ya traducido, con el renglón («¿Dar de baja a Ana López?»)\n * @param {import('react').ReactNode} [props.description] qué va a pasar, en palabras normales\n * @param {string} [props.confirmLabel]\n * @param {string} [props.cancelLabel]\n * @param {'primary'|'error'|'warning'} [props.tone]\n * @param {boolean} [props.isPending]\n * @param {boolean} [props.isConfirmDisabled] la acción ya no aplica (p. ej. conflicto: el registro cambió)\n * @param {string|null} [props.errorMessage] mensaje ya traducido (ApiError.message)\n * @param {() => void} props.onConfirm\n * @param {() => void} props.onClose\n * @param {import('react').ReactNode} [props.children]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.js","sources":["../../src/feedback/ConfirmDialog.jsx"],"sourcesContent":["// Diálogo de confirmación: toda acción que cambia algo, manda un correo o descarga un archivo pasa por aquí antes de\n// ejecutarse (regla de tablas). Dice en lenguaje ciudadano QUÉ va a pasar y a QUÉ renglón («¿Dar de baja a Ana López?\n// Ya no podrá entrar; se puede reactivar después»).\n//\n// Recibe el estado de la petición por props (pending, error) en vez de hacerla él: core no conoce el API. Lo normal es\n// alimentarlo con useConfirmAction (máquina confirmActionMachine), que ya garantiza que cancelar no ejecuta y que un\n// doble clic no manda dos peticiones. Mientras la petición está en curso no se puede cerrar ni volver a confirmar.\n//\n// children: lo que el diálogo necesita pedir antes de confirmar (p. ej. el motivo de una baja), debajo del texto.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaConfirmDialog con defaultProps y\n// styleOverrides de sus partes: problem, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Button from '@mui/material/Button';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\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.CalcitaConfirmDialog)\nconst THEME_NAME = 'CalcitaConfirmDialog';\nconst ConfirmProblem = piecePart(THEME_NAME, 'Problem', Alert);\nconst ConfirmActions = piecePart(THEME_NAME, 'Actions', DialogActions);\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {string} props.title texto ya traducido, con el renglón («¿Dar de baja a Ana López?»)\n * @param {import('react').ReactNode} [props.description] qué va a pasar, en palabras normales\n * @param {string} [props.confirmLabel]\n * @param {string} [props.cancelLabel]\n * @param {'primary'|'error'|'warning'} [props.tone]\n * @param {boolean} [props.isPending]\n * @param {boolean} [props.isConfirmDisabled] la acción ya no aplica (p. ej. conflicto: el registro cambió)\n * @param {string|null} [props.errorMessage] mensaje ya traducido (ApiError.message)\n * @param {() => void} props.onConfirm\n * @param {() => void} props.onClose\n * @param {import('react').ReactNode} [props.children]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction ConfirmDialogContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaConfirmDialog.defaultProps) y encima lo que llega aquí\n const {\n open, title, description, confirmLabel, cancelLabel, tone = 'primary',\n isPending = false, isConfirmDisabled = false, errorMessage = null, onConfirm, onClose, children, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n // ids propios: una pantalla puede tener varios diálogos montados y los aria-* no deben apuntar al de otro\n const dialogId = useId();\n\n const handleClose = () => {\n // guard clause: no se cierra a media petición\n if (isPending) {\n return;\n }\n onClose();\n };\n\n return (\n <Dialog open={open} onClose={handleClose} maxWidth=\"xs\" fullWidth\n aria-labelledby={`${dialogId}-title`} aria-describedby={`${dialogId}-description`}>\n <DialogTitle id={`${dialogId}-title`}>{title}</DialogTitle>\n <DialogContent>\n {errorMessage && <ConfirmProblem severity=\"error\" role=\"alert\" ownSx={{mb: 2}}>{errorMessage}</ConfirmProblem>}\n {description && (\n <DialogContentText id={`${dialogId}-description`}>{description}</DialogContentText>\n )}\n {children}\n </DialogContent>\n <ConfirmActions ownSx={{px: 3, pb: 2.5}}>\n <Button variant=\"outlined\" color=\"inherit\" onClick={handleClose} disabled={isPending}>\n {cancelLabel ?? t('actions.cancel')}\n </Button>\n <Button variant=\"contained\" color={tone} onClick={onConfirm} disabled={isPending || isConfirmDisabled}\n aria-busy={isPending}>\n {isPending ? <CircularProgress size={20} color=\"inherit\" /> : (confirmLabel ?? t('actions.confirm'))}\n </Button>\n </ConfirmActions>\n </Dialog>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst ConfirmDialog = withPieceSlots(ConfirmDialogContent, THEME_NAME);\nexport default ConfirmDialog;\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA4BA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,WAAW,KAAK;AAC7D,MAAM,iBAAiB,UAAU,YAAY,WAAW,aAAa;AAkBrE,SAAS,qBAAqB,OAAO;AAEjC,QAAM;AAAA,IACN;AAAA,IAAM;AAAA,IAAO;AAAA,IAAa;AAAA,IAAc;AAAA,IAAa,OAAO;AAAA,IAC5D,YAAY;AAAA,IAAO,oBAAoB;AAAA,IAAO,eAAe;AAAA,IAAM;AAAA,IAAW;AAAA,IAAS;AAAA,IAAU;AAAA,IACjG,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AAC/B,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,QAAM,WAAW,MAAA;AAEjB,QAAM,cAAc,MAAM;AAEtB,QAAI,WAAW;AACX;AAAA,IACJ;AACA,YAAA;AAAA,EACJ;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAO;AAAA,MAAY,SAAS;AAAA,MAAa,UAAS;AAAA,MAAK,WAAS;AAAA,MACzD,mBAAiB,GAAG,QAAQ;AAAA,MAAU,oBAAkB,GAAG,QAAQ;AAAA,MACvE,UAAA;AAAA,QAAA,oBAAC,aAAA,EAAY,IAAI,GAAG,QAAQ,UAAW,UAAA,OAAM;AAAA,6BAC5C,eAAA,EACI,UAAA;AAAA,UAAA,gBAAgB,oBAAC,gBAAA,EAAe,UAAS,SAAQ,MAAK,SAAQ,OAAO,EAAC,IAAI,EAAA,GAAK,UAAA,aAAA,CAAa;AAAA,UAC5F,eACG,oBAAC,mBAAA,EAAkB,IAAI,GAAG,QAAQ,gBAAiB,UAAA,aAAY;AAAA,UAElE;AAAA,QAAA,GACL;AAAA,QACA,qBAAC,kBAAe,OAAO,EAAC,IAAI,GAAG,IAAI,OAC/B,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAO,SAAQ,YAAW,OAAM,WAAU,SAAS,aAAa,UAAU,WACtE,UAAA,eAAe,EAAE,gBAAgB,GACtC;AAAA,UACA;AAAA,YAAC;AAAA,YAAA;AAAA,cAAO,SAAQ;AAAA,cAAY,OAAO;AAAA,cAAM,SAAS;AAAA,cAAW,UAAU,aAAa;AAAA,cAC5E,aAAW;AAAA,cACd,UAAA,YAAY,oBAAC,kBAAA,EAAiB,MAAM,IAAI,OAAM,UAAA,CAAU,IAAM,gBAAgB,EAAE,iBAAiB;AAAA,YAAA;AAAA,UAAA;AAAA,QACtG,EAAA,CACJ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGZ;AAGA,MAAM,gBAAgB,eAAe,sBAAsB,UAAU;"}
|
|
@@ -7,14 +7,14 @@ import DialogContent from "@mui/material/DialogContent";
|
|
|
7
7
|
import DialogContentText from "@mui/material/DialogContentText";
|
|
8
8
|
import DialogTitle from "@mui/material/DialogTitle";
|
|
9
9
|
import Stack from "@mui/material/Stack";
|
|
10
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
10
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
11
11
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
12
12
|
const THEME_NAME = "CalcitaModalWaiting";
|
|
13
13
|
const WaitingRoot = piecePart(THEME_NAME, "Root", Dialog);
|
|
14
14
|
const WaitingTitle = piecePart(THEME_NAME, "Title", DialogTitle);
|
|
15
15
|
const WaitingBody = piecePart(THEME_NAME, "Body", Stack);
|
|
16
16
|
const WaitingText = piecePart(THEME_NAME, "Text", DialogContentText);
|
|
17
|
-
function
|
|
17
|
+
function ModalWaitingContent(props) {
|
|
18
18
|
const { open, title, text, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
19
19
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
20
20
|
const { t } = useToolkitTranslation("common", texts);
|
|
@@ -26,6 +26,7 @@ function ModalWaiting(props) {
|
|
|
26
26
|
] }) })
|
|
27
27
|
] });
|
|
28
28
|
}
|
|
29
|
+
const ModalWaiting = withPieceSlots(ModalWaitingContent, THEME_NAME);
|
|
29
30
|
export {
|
|
30
31
|
ModalWaiting as default
|
|
31
32
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModalWaiting.js","sources":["../../src/feedback/ModalWaiting.jsx"],"sourcesContent":["// Bloqueo general \"espere un momento\" como diálogo modal, para una acción que no se puede interrumpir (p. ej. generar\n// un archivo grande).\n//\n// OJO zIndex: HASTA ARRIBA de todo, encima de modal (1300), snackbar (1400) y tooltip (1500), para que ningún elemento\n// de la interfaz se le monte mientras bloquea. No se cierra con Escape a propósito.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaModalWaiting con defaultProps y\n// styleOverrides de sus partes: root, title, body, text.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Dialog from '@mui/material/Dialog';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Stack from '@mui/material/Stack';\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.CalcitaModalWaiting)\nconst THEME_NAME = 'CalcitaModalWaiting';\nconst WaitingRoot = piecePart(THEME_NAME, 'Root', Dialog);\nconst WaitingTitle = piecePart(THEME_NAME, 'Title', DialogTitle);\nconst WaitingBody = piecePart(THEME_NAME, 'Body', Stack);\nconst WaitingText = piecePart(THEME_NAME, 'Text', DialogContentText);\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {string} [props.title] ya traducido; por omisión common:waiting.title\n * @param {string} [props.text] ya traducido; por omisión common:waiting.text\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"ModalWaiting.js","sources":["../../src/feedback/ModalWaiting.jsx"],"sourcesContent":["// Bloqueo general \"espere un momento\" como diálogo modal, para una acción que no se puede interrumpir (p. ej. generar\n// un archivo grande).\n//\n// OJO zIndex: HASTA ARRIBA de todo, encima de modal (1300), snackbar (1400) y tooltip (1500), para que ningún elemento\n// de la interfaz se le monte mientras bloquea. No se cierra con Escape a propósito.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaModalWaiting con defaultProps y\n// styleOverrides de sus partes: root, title, body, text.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Dialog from '@mui/material/Dialog';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogContentText from '@mui/material/DialogContentText';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Stack from '@mui/material/Stack';\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.CalcitaModalWaiting)\nconst THEME_NAME = 'CalcitaModalWaiting';\nconst WaitingRoot = piecePart(THEME_NAME, 'Root', Dialog);\nconst WaitingTitle = piecePart(THEME_NAME, 'Title', DialogTitle);\nconst WaitingBody = piecePart(THEME_NAME, 'Body', Stack);\nconst WaitingText = piecePart(THEME_NAME, 'Text', DialogContentText);\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {string} [props.title] ya traducido; por omisión common:waiting.title\n * @param {string} [props.text] ya traducido; por omisión common:waiting.text\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction ModalWaitingContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaModalWaiting.defaultProps) y encima lo que llega aquí\n const {open, title, text, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n\n return (\n <WaitingRoot open={open} disableEscapeKeyDown aria-busy=\"true\" ownSx={{zIndex: (theme) => theme.zIndex.tooltip + 10}}>\n <WaitingTitle ownSx={{textAlign: 'center'}}>{title ?? t('waiting.title')}</WaitingTitle>\n <DialogContent>\n <WaitingBody spacing={2} ownSx={{alignItems: 'center'}}>\n <CircularProgress size={28} />\n <WaitingText ownSx={{textAlign: 'center'}}>{text ?? t('waiting.text')}</WaitingText>\n </WaitingBody>\n </DialogContent>\n </WaitingRoot>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst ModalWaiting = withPieceSlots(ModalWaitingContent, THEME_NAME);\nexport default ModalWaiting;\n"],"names":[],"mappings":";;;;;;;;;;;AAqBA,MAAM,aAAa;AACnB,MAAM,cAAc,UAAU,YAAY,QAAQ,MAAM;AACxD,MAAM,eAAe,UAAU,YAAY,SAAS,WAAW;AAC/D,MAAM,cAAc,UAAU,YAAY,QAAQ,KAAK;AACvD,MAAM,cAAc,UAAU,YAAY,QAAQ,iBAAiB;AASnE,SAAS,oBAAoB,OAAO;AAEhC,QAAM,EAAC,MAAM,OAAO,MAAM,OAAO,GAAG,iBAAgB,cAAc,OAAO,UAAU;AACnF,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,8BACK,aAAA,EAAY,MAAY,sBAAoB,MAAC,aAAU,QAAO,OAAO,EAAC,QAAQ,CAAC,UAAU,MAAM,OAAO,UAAU,MAC7G,UAAA;AAAA,IAAA,oBAAC,cAAA,EAAa,OAAO,EAAC,WAAW,YAAY,UAAA,SAAS,EAAE,eAAe,EAAA,CAAE;AAAA,IACzE,oBAAC,eAAA,EACG,UAAA,qBAAC,aAAA,EAAY,SAAS,GAAG,OAAO,EAAC,YAAY,SAAA,GACzC,UAAA;AAAA,MAAA,oBAAC,kBAAA,EAAiB,MAAM,GAAA,CAAI;AAAA,MAC5B,oBAAC,aAAA,EAAY,OAAO,EAAC,WAAW,YAAY,UAAA,QAAQ,EAAE,cAAc,EAAA,CAAE;AAAA,IAAA,EAAA,CAC1E,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;AAGA,MAAM,eAAe,eAAe,qBAAqB,UAAU;"}
|
|
@@ -11,7 +11,7 @@ import { aboveMobileTabBarOf, GLASS_PANEL_GAP_PIXELS } from "../appShell/shellSi
|
|
|
11
11
|
import { minimalFloatingStylesOf } from "../theme/components/minimalComponents.js";
|
|
12
12
|
import { isGlassTheme, glassStylesOf } from "../theme/glassSurface.js";
|
|
13
13
|
import { APP_BAR_HEIGHT } from "../ui/layout/appBarHeight.js";
|
|
14
|
-
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
14
|
+
import { piecePart, withPieceSlots, usePieceProps } from "../theme/themePieces.js";
|
|
15
15
|
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
16
16
|
const THEME_NAME = "CalcitaNewBuildNotice";
|
|
17
17
|
const NoticeFloating = piecePart(THEME_NAME, "Floating", Box);
|
|
@@ -53,7 +53,7 @@ function floatingSxOf(theme) {
|
|
|
53
53
|
"@media (prefers-reduced-motion: reduce)": { animation: "none" }
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
function
|
|
56
|
+
function NewBuildNoticeContent(props) {
|
|
57
57
|
const { message, reloadLabel, onReload = reloadPage, variant = NEW_BUILD_NOTICE_VARIANT.ribbon, texts, ...unknownProps } = usePieceProps(props, THEME_NAME);
|
|
58
58
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
59
59
|
const { t } = useToolkitTranslation("common", texts);
|
|
@@ -82,6 +82,7 @@ function NewBuildNotice(props) {
|
|
|
82
82
|
}
|
|
83
83
|
);
|
|
84
84
|
}
|
|
85
|
+
const NewBuildNotice = withPieceSlots(NewBuildNoticeContent, THEME_NAME);
|
|
85
86
|
export {
|
|
86
87
|
NEW_BUILD_NOTICE_VARIANT,
|
|
87
88
|
NewBuildNotice as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NewBuildNotice.js","sources":["../../src/feedback/NewBuildNotice.jsx"],"sourcesContent":["// Aviso «Hay una versión nueva» con su botón de recargar.\n//\n// Sale cuando el servidor anuncia una compilación del front distinta a la que corre en la pestaña\n// (observability/publishedBuild.js) y se queda hasta que la persona recarga. No recarga solo: la persona puede estar a\n// media captura y decide cuándo.\n//\n// Dos formas, el mismo aviso:\n// - «ribbon» (por omisión): una franja a todo lo ancho que el proyecto pone donde se vea siempre, normalmente dentro de\n// su AppBar, debajo de la barra. Se arma con el Alert de MUI, que ya trae sus colores para claro y oscuro.\n// - «floating»: una cápsula de vidrio que flota aparte, sin tocar la barra, como el reproductor chico que Apple pone\n// encima de las pestañas en el iPhone. En escritorio, centrada debajo de la barra de arriba; en el teléfono, encima de\n// la barra de pestañas. Es la que usa AppLayout con vidrio: dentro de la cápsula de la barra la franja la hacía crecer al\n// doble, la curva cortaba el texto y el botón se salía por la orilla (lo reportaron).\n// Sin compilación nueva no pinta nada ni ocupa lugar.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaNewBuildNotice con defaultProps y\n// styleOverrides de sus partes: floating, icon, message, reloadButton, banner.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Typography from '@mui/material/Typography';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\n\nimport {useNewBuildPublished} from '@/observability/publishedBuild.js';\nimport {GLASS_PANEL_GAP_PIXELS, aboveMobileTabBarOf} from '@/appShell/shellSizes.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {APP_BAR_HEIGHT} from '@/ui/layout/appBarHeight.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaNewBuildNotice)\nconst THEME_NAME = 'CalcitaNewBuildNotice';\nconst NoticeFloating = piecePart(THEME_NAME, 'Floating', Box);\nconst NoticeIcon = piecePart(THEME_NAME, 'Icon', RefreshRoundedIcon);\nconst NoticeMessage = piecePart(THEME_NAME, 'Message', Typography);\nconst NoticeReloadButton = piecePart(THEME_NAME, 'ReloadButton', Button);\nconst NoticeBanner = piecePart(THEME_NAME, 'Banner', Alert);\n\n/** Las formas del aviso: franja (dentro de la barra del proyecto) o cápsula flotante (la del marco con vidrio). */\nexport const NEW_BUILD_NOTICE_VARIANT = Object.freeze({ribbon: 'ribbon', floating: 'floating'});\n\n// lo que se separa la cápsula de la barra (px) y lo que tarda en aparecer (ms)\nconst FLOATING_GAP_PIXELS = 8;\nconst FLOATING_ENTER_MILLISECONDS = 280;\n\n/** Lo que hace el botón si el proyecto no dice otra cosa: recargar la página, que trae la compilación nueva. */\nfunction reloadPage() {\n window.location.reload();\n}\n\n/**\n * La cápsula flotante: fija a la ventana y centrada. En escritorio queda debajo de la barra de arriba (su alto real,\n * APP_BAR_HEIGHT); en el teléfono, encima de la barra de pestañas (su alto, sus separaciones y la zona del gesto de inicio).\n *\n * Va centrada en la VENTANA, debajo del centro de la barra (que va de orilla a orilla): centrada en el contenido quedaba\n * corrida a la derecha respecto a la barra (lo reportaron).\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction floatingSxOf(theme) {\n return {\n // con vidrio, cápsula de vidrio; con el minimalista (barra en cápsulas), sólida del gris de lo que flota\n ...(isGlassTheme(theme) ? glassStylesOf(theme) : minimalFloatingStylesOf(theme)),\n position: 'fixed',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: theme.zIndex.appBar,\n display: 'flex',\n alignItems: 'center',\n gap: 1.5,\n width: 'max-content',\n maxWidth: `calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px)`,\n borderRadius: '999px',\n py: 0.75,\n pl: 2,\n pr: 0.75,\n top: `calc(${APP_BAR_HEIGHT} + ${FLOATING_GAP_PIXELS}px)`,\n ...aboveMobileTabBarOf(FLOATING_GAP_PIXELS),\n // aparece subiendo un poco y aclarándose; sin movimiento si la persona lo pidió así en su equipo\n '@keyframes calcitaNoticeEnter': {\n from: {opacity: 0, transform: 'translate(-50%, 8px) scale(0.96)'},\n to: {opacity: 1, transform: 'translate(-50%, 0) scale(1)'},\n },\n animation: `calcitaNoticeEnter ${FLOATING_ENTER_MILLISECONDS}ms ${theme.transitions.easing.easeOut}`,\n '@media (prefers-reduced-motion: reduce)': {animation: 'none'},\n };\n}\n\n/**\n * @param {object} [props]\n * @param {string} [props.message] ya traducido; por omisión el del paquete (en la cápsula, el corto: common:newBuild.short)\n * @param {string} [props.reloadLabel] ya traducido; por omisión «Recargar»\n * @param {() => void} [props.onReload] por omisión recarga la página\n * @param {'ribbon'|'floating'} [props.variant] franja o cápsula flotante (NEW_BUILD_NOTICE_VARIANT); por omisión franja\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\
|
|
1
|
+
{"version":3,"file":"NewBuildNotice.js","sources":["../../src/feedback/NewBuildNotice.jsx"],"sourcesContent":["// Aviso «Hay una versión nueva» con su botón de recargar.\n//\n// Sale cuando el servidor anuncia una compilación del front distinta a la que corre en la pestaña\n// (observability/publishedBuild.js) y se queda hasta que la persona recarga. No recarga solo: la persona puede estar a\n// media captura y decide cuándo.\n//\n// Dos formas, el mismo aviso:\n// - «ribbon» (por omisión): una franja a todo lo ancho que el proyecto pone donde se vea siempre, normalmente dentro de\n// su AppBar, debajo de la barra. Se arma con el Alert de MUI, que ya trae sus colores para claro y oscuro.\n// - «floating»: una cápsula de vidrio que flota aparte, sin tocar la barra, como el reproductor chico que Apple pone\n// encima de las pestañas en el iPhone. En escritorio, centrada debajo de la barra de arriba; en el teléfono, encima de\n// la barra de pestañas. Es la que usa AppLayout con vidrio: dentro de la cápsula de la barra la franja la hacía crecer al\n// doble, la curva cortaba el texto y el botón se salía por la orilla (lo reportaron).\n// Sin compilación nueva no pinta nada ni ocupa lugar.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaNewBuildNotice con defaultProps y\n// styleOverrides de sus partes: floating, icon, message, reloadButton, banner.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Typography from '@mui/material/Typography';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\n\nimport {useNewBuildPublished} from '@/observability/publishedBuild.js';\nimport {GLASS_PANEL_GAP_PIXELS, aboveMobileTabBarOf} from '@/appShell/shellSizes.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {APP_BAR_HEIGHT} from '@/ui/layout/appBarHeight.js';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaNewBuildNotice)\nconst THEME_NAME = 'CalcitaNewBuildNotice';\nconst NoticeFloating = piecePart(THEME_NAME, 'Floating', Box);\nconst NoticeIcon = piecePart(THEME_NAME, 'Icon', RefreshRoundedIcon);\nconst NoticeMessage = piecePart(THEME_NAME, 'Message', Typography);\nconst NoticeReloadButton = piecePart(THEME_NAME, 'ReloadButton', Button);\nconst NoticeBanner = piecePart(THEME_NAME, 'Banner', Alert);\n\n/** Las formas del aviso: franja (dentro de la barra del proyecto) o cápsula flotante (la del marco con vidrio). */\nexport const NEW_BUILD_NOTICE_VARIANT = Object.freeze({ribbon: 'ribbon', floating: 'floating'});\n\n// lo que se separa la cápsula de la barra (px) y lo que tarda en aparecer (ms)\nconst FLOATING_GAP_PIXELS = 8;\nconst FLOATING_ENTER_MILLISECONDS = 280;\n\n/** Lo que hace el botón si el proyecto no dice otra cosa: recargar la página, que trae la compilación nueva. */\nfunction reloadPage() {\n window.location.reload();\n}\n\n/**\n * La cápsula flotante: fija a la ventana y centrada. En escritorio queda debajo de la barra de arriba (su alto real,\n * APP_BAR_HEIGHT); en el teléfono, encima de la barra de pestañas (su alto, sus separaciones y la zona del gesto de inicio).\n *\n * Va centrada en la VENTANA, debajo del centro de la barra (que va de orilla a orilla): centrada en el contenido quedaba\n * corrida a la derecha respecto a la barra (lo reportaron).\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction floatingSxOf(theme) {\n return {\n // con vidrio, cápsula de vidrio; con el minimalista (barra en cápsulas), sólida del gris de lo que flota\n ...(isGlassTheme(theme) ? glassStylesOf(theme) : minimalFloatingStylesOf(theme)),\n position: 'fixed',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: theme.zIndex.appBar,\n display: 'flex',\n alignItems: 'center',\n gap: 1.5,\n width: 'max-content',\n maxWidth: `calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px)`,\n borderRadius: '999px',\n py: 0.75,\n pl: 2,\n pr: 0.75,\n top: `calc(${APP_BAR_HEIGHT} + ${FLOATING_GAP_PIXELS}px)`,\n ...aboveMobileTabBarOf(FLOATING_GAP_PIXELS),\n // aparece subiendo un poco y aclarándose; sin movimiento si la persona lo pidió así en su equipo\n '@keyframes calcitaNoticeEnter': {\n from: {opacity: 0, transform: 'translate(-50%, 8px) scale(0.96)'},\n to: {opacity: 1, transform: 'translate(-50%, 0) scale(1)'},\n },\n animation: `calcitaNoticeEnter ${FLOATING_ENTER_MILLISECONDS}ms ${theme.transitions.easing.easeOut}`,\n '@media (prefers-reduced-motion: reduce)': {animation: 'none'},\n };\n}\n\n/**\n * @param {object} [props]\n * @param {string} [props.message] ya traducido; por omisión el del paquete (en la cápsula, el corto: common:newBuild.short)\n * @param {string} [props.reloadLabel] ya traducido; por omisión «Recargar»\n * @param {() => void} [props.onReload] por omisión recarga la página\n * @param {'ribbon'|'floating'} [props.variant] franja o cápsula flotante (NEW_BUILD_NOTICE_VARIANT); por omisión franja\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction NewBuildNoticeContent(props) {\n // lo que el proyecto puso en su tema (components.CalcitaNewBuildNotice.defaultProps) y encima lo que llega aquí\n const {message, reloadLabel, onReload = reloadPage, variant = NEW_BUILD_NOTICE_VARIANT.ribbon, texts, ...unknownProps} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const isNewBuildPublished = useNewBuildPublished();\n\n // guard clause: la compilación de la pestaña sigue siendo la publicada; el aviso no ocupa lugar\n if (!isNewBuildPublished) {\n return null;\n }\n\n const buttonLabel = reloadLabel ?? t('newBuild.reload');\n\n if (variant === NEW_BUILD_NOTICE_VARIANT.floating) {\n // el texto largo va para el lector de pantalla; a la vista, el corto (una cápsula de un renglón)\n return (\n <NoticeFloating role=\"status\" aria-live=\"polite\" aria-label={message ?? t('newBuild.message')} ownSx={floatingSxOf}>\n <NoticeIcon fontSize=\"small\" ownSx={{color: 'text.secondary'}} />\n <NoticeMessage variant=\"body2\" noWrap ownSx={{fontWeight: 500, minWidth: 0}}>{message ?? t('newBuild.short')}</NoticeMessage>\n <NoticeReloadButton size=\"small\" variant=\"contained\" onClick={onReload} ownSx={{flexShrink: 0}}>{buttonLabel}</NoticeReloadButton>\n </NoticeFloating>\n );\n }\n\n return (\n <NoticeBanner severity=\"warning\" square role=\"status\" aria-live=\"polite\"\n ownSx={{py: 0, px: {xs: 2, md: 3}, alignItems: 'center'}}\n action={(\n <NoticeReloadButton size=\"small\" variant=\"outlined\" color=\"inherit\" onClick={onReload} ownSx={{flexShrink: 0}}>\n {buttonLabel}\n </NoticeReloadButton>\n )}>\n {message ?? t('newBuild.message')}\n </NoticeBanner>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst NewBuildNotice = withPieceSlots(NewBuildNoticeContent, THEME_NAME);\nexport default NewBuildNotice;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AAmCA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,YAAY,GAAG;AAC5D,MAAM,aAAa,UAAU,YAAY,QAAQ,kBAAkB;AACnE,MAAM,gBAAgB,UAAU,YAAY,WAAW,UAAU;AACjE,MAAM,qBAAqB,UAAU,YAAY,gBAAgB,MAAM;AACvE,MAAM,eAAe,UAAU,YAAY,UAAU,KAAK;AAGnD,MAAM,2BAA2B,OAAO,OAAO,EAAC,QAAQ,UAAU,UAAU,YAAW;AAG9F,MAAM,sBAAsB;AAC5B,MAAM,8BAA8B;AAGpC,SAAS,aAAa;AAClB,SAAO,SAAS,OAAA;AACpB;AAYA,SAAS,aAAa,OAAO;AACzB,SAAO;AAAA;AAAA,IAEH,GAAI,aAAa,KAAK,IAAI,cAAc,KAAK,IAAI,wBAAwB,KAAK;AAAA,IAC9E,UAAU;AAAA,IACV,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ,MAAM,OAAO;AAAA,IACrB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,OAAO;AAAA,IACP,UAAU,eAAe,yBAAyB,CAAC;AAAA,IACnD,cAAc;AAAA,IACd,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,KAAK,QAAQ,cAAc,MAAM,mBAAmB;AAAA,IACpD,GAAG,oBAAoB,mBAAmB;AAAA;AAAA,IAE1C,iCAAiC;AAAA,MAC7B,MAAM,EAAC,SAAS,GAAG,WAAW,mCAAA;AAAA,MAC9B,IAAI,EAAC,SAAS,GAAG,WAAW,8BAAA;AAAA,IAA6B;AAAA,IAE7D,WAAW,sBAAsB,2BAA2B,MAAM,MAAM,YAAY,OAAO,OAAO;AAAA,IAClG,2CAA2C,EAAC,WAAW,OAAA;AAAA,EAAM;AAErE;AAUA,SAAS,sBAAsB,OAAO;AAElC,QAAM,EAAC,SAAS,aAAa,WAAW,YAAY,UAAU,yBAAyB,QAAQ,OAAO,GAAG,aAAA,IAAgB,cAAc,OAAO,UAAU;AACxJ,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,sBAAsB,qBAAA;AAG5B,MAAI,CAAC,qBAAqB;AACtB,WAAO;AAAA,EACX;AAEA,QAAM,cAAc,eAAe,EAAE,iBAAiB;AAEtD,MAAI,YAAY,yBAAyB,UAAU;AAE/C,WACI,qBAAC,gBAAA,EAAe,MAAK,UAAS,aAAU,UAAS,cAAY,WAAW,EAAE,kBAAkB,GAAG,OAAO,cAClG,UAAA;AAAA,MAAA,oBAAC,cAAW,UAAS,SAAQ,OAAO,EAAC,OAAO,oBAAmB;AAAA,0BAC9D,eAAA,EAAc,SAAQ,SAAQ,QAAM,MAAC,OAAO,EAAC,YAAY,KAAK,UAAU,KAAK,UAAA,WAAW,EAAE,gBAAgB,GAAE;AAAA,MAC7G,oBAAC,oBAAA,EAAmB,MAAK,SAAQ,SAAQ,aAAY,SAAS,UAAU,OAAO,EAAC,YAAY,EAAA,GAAK,UAAA,YAAA,CAAY;AAAA,IAAA,GACjH;AAAA,EAER;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAa,UAAS;AAAA,MAAU,QAAM;AAAA,MAAC,MAAK;AAAA,MAAS,aAAU;AAAA,MACzD,OAAO,EAAC,IAAI,GAAG,IAAI,EAAC,IAAI,GAAG,IAAI,KAAI,YAAY,SAAA;AAAA,MAC/C,QACI,oBAAC,oBAAA,EAAmB,MAAK,SAAQ,SAAQ,YAAW,OAAM,WAAU,SAAS,UAAU,OAAO,EAAC,YAAY,KACtG,UAAA,aACL;AAAA,MAEN,UAAA,WAAW,EAAE,kBAAkB;AAAA,IAAA;AAAA,EAAA;AAG5C;AAGA,MAAM,iBAAiB,eAAe,uBAAuB,UAAU;"}
|