@stackcraftmx/calcita 0.23.0 → 0.26.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 +127 -22
- package/cli/projectFiles.js +5 -3
- package/cli/templates/project/README.md.tpl +28 -0
- package/cli/templates/project/public/manifest.webmanifest.tpl +6 -6
- package/cli/templates/project/src/app/AppProviders.jsx.tpl +9 -4
- package/cli/templates/project/src/app/appRouter.jsx.tpl +4 -2
- package/cli/templates/project/src/app/appSettings.js.tpl +14 -0
- package/cli/templates/project/src/core/AppLogo.jsx.tpl +3 -1
- package/cli/templates/project/src/core/index.js.tpl +1 -0
- package/cli/templates/project/src/core/publicPaths.js.tpl +24 -0
- package/cli/templates/project/vite.config.js.tpl +27 -1
- package/dist/DataTable/AuditTrailTable.js +5 -3
- package/dist/DataTable/AuditTrailTable.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +2 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/EmptyState.js +2 -2
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +2 -2
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +4 -3
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +153 -83
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/rowSelection.js +61 -0
- package/dist/DataTable/rowSelection.js.map +1 -0
- package/dist/access/PermissionMatrix.js +3 -2
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/account/AccountSecuritySection.js +2 -2
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/ForgotPasswordScreen.js +5 -4
- package/dist/account/ForgotPasswordScreen.js.map +1 -1
- package/dist/account/ProfileScreen.js +39 -13
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/ResetPasswordScreen.js +3 -2
- package/dist/account/ResetPasswordScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +7 -6
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/account/accountConnector.js.map +1 -1
- package/dist/account/restAccountConnector.js +2 -1
- package/dist/account/restAccountConnector.js.map +1 -1
- package/dist/adapters/reactRouterRoutes.js +7 -6
- package/dist/adapters/reactRouterRoutes.js.map +1 -1
- package/dist/api/createApiClient.js +40 -5
- package/dist/api/createApiClient.js.map +1 -1
- package/dist/appShell/AccountMenu.js +15 -23
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +42 -15
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +29 -6
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +2 -0
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActionButton.js +3 -3
- package/dist/appShell/BarActionButton.js.map +1 -1
- package/dist/appShell/BarActions.js +19 -8
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarCapsule.js +19 -4
- package/dist/appShell/BarCapsule.js.map +1 -1
- package/dist/appShell/BarSearch.js +92 -0
- package/dist/appShell/BarSearch.js.map +1 -0
- package/dist/appShell/HeaderTools.js +4 -2
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MenuLinks.js +36 -0
- package/dist/appShell/MenuLinks.js.map +1 -0
- package/dist/appShell/MobileTabBar.js +6 -4
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +10 -5
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +5 -3
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +3 -3
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +3 -4
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TopMenu.js +5 -6
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +11 -0
- package/dist/appShell/Wordmark.js.map +1 -0
- package/dist/appShell/menuItemText.js +4 -0
- package/dist/appShell/menuItemText.js.map +1 -1
- package/dist/appShell/shellSizes.js +2 -0
- package/dist/appShell/shellSizes.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +2 -2
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +16 -5
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +74 -0
- package/dist/calendar/EventDetails.js.map +1 -0
- package/dist/calendar/calendarEvents.js +31 -0
- package/dist/calendar/calendarEvents.js.map +1 -0
- package/dist/calendar.js +8 -1
- package/dist/calendar.js.map +1 -1
- package/dist/charts.js +10 -1
- package/dist/charts.js.map +1 -1
- package/dist/datePickers/DateInput.js +3 -2
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/downloads/DownloadManager.js +14 -20
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/downloads/downloadQueue.js +4 -2
- package/dist/downloads/downloadQueue.js.map +1 -1
- package/dist/downloads/downloadsMachine.js +4 -1
- package/dist/downloads/downloadsMachine.js.map +1 -1
- package/dist/downloads/fileTypes.js +22 -0
- package/dist/downloads/fileTypes.js.map +1 -0
- package/dist/feedback/ConfirmDialog.js +3 -2
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +2 -2
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +2 -2
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +5 -3
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/formatting.js +171 -143
- package/dist/formatting.js.map +1 -1
- package/dist/forms/FormWizard.js +18 -45
- package/dist/forms/FormWizard.js.map +1 -1
- package/dist/forms/auth/ChangePasswordForm.js +5 -4
- package/dist/forms/auth/ChangePasswordForm.js.map +1 -1
- package/dist/forms/auth/ForgotPasswordForm.js +2 -2
- package/dist/forms/auth/ForgotPasswordForm.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +2 -2
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/security/SecondStepForm.js +2 -2
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +8 -6
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/hookForm/FormWizard.js +59 -0
- package/dist/hookForm/FormWizard.js.map +1 -0
- package/dist/inbox/InboxLayout.js +44 -8
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +71 -21
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +80 -29
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/inbox/inboxDates.js +4 -3
- package/dist/inbox/inboxDates.js.map +1 -1
- package/dist/index.js +163 -136
- package/dist/index.js.map +1 -1
- package/dist/inputs/CatalogInput.js +8 -4
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +3 -2
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FormSection.js +3 -3
- package/dist/inputs/FormSection.js.map +1 -1
- package/dist/inputs/NumberInput.js +3 -2
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/SelectInput.js +29 -11
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +6 -2
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +54 -0
- package/dist/inputs/WizardLayout.js.map +1 -0
- package/dist/kanban/KanbanBoard.js +239 -14
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/kanban/kanbanCards.js +19 -0
- package/dist/kanban/kanbanCards.js.map +1 -0
- package/dist/kanban.js +7 -1
- package/dist/kanban.js.map +1 -1
- package/dist/locales/en.js +74 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +74 -1
- package/dist/locales/es.js.map +1 -1
- package/dist/maps/MapView.js +28 -8
- package/dist/maps/MapView.js.map +1 -1
- package/dist/navigation/appMenu.js +13 -1
- package/dist/navigation/appMenu.js.map +1 -1
- package/dist/observability/logger.js +18 -0
- package/dist/observability/logger.js.map +1 -1
- package/dist/observability/usePublishedBuildWatch.js +4 -3
- package/dist/observability/usePublishedBuildWatch.js.map +1 -1
- package/dist/pwa/InstallAppSetting.js +6 -5
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +2 -2
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +15 -9
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/pwa/useInstallPrompt.js +2 -1
- package/dist/pwa/useInstallPrompt.js.map +1 -1
- package/dist/react-hook-form.js +6 -4
- package/dist/react-hook-form.js.map +1 -1
- package/dist/security/ActivityLog.js +4 -3
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +4 -3
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +8 -6
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/table.js +2 -0
- package/dist/table.js.map +1 -1
- package/dist/theme/components/MuiAppBar.js +6 -5
- package/dist/theme/components/MuiAppBar.js.map +1 -1
- package/dist/theme/components/MuiDialog.js +13 -0
- package/dist/theme/components/MuiDialog.js.map +1 -0
- package/dist/theme/components/glassComponents.js +32 -31
- package/dist/theme/components/glassComponents.js.map +1 -1
- package/dist/theme/components/minimalComponents.js +8 -4
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/theme/createToolkitTheme.js.map +1 -1
- package/dist/theme/glassSurface.js +36 -38
- package/dist/theme/glassSurface.js.map +1 -1
- package/dist/theme/lightDarkStyles.js +48 -0
- package/dist/theme/lightDarkStyles.js.map +1 -0
- package/dist/theme/toolkitComponents.js +2 -0
- package/dist/theme/toolkitComponents.js.map +1 -1
- package/dist/toolkit/ComponentTexts.js +13 -0
- package/dist/toolkit/ComponentTexts.js.map +1 -0
- package/dist/toolkit/ToolkitProvider.js +36 -28
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/browserNavigation.js +19 -20
- package/dist/toolkit/browserNavigation.js.map +1 -1
- package/dist/toolkit/componentTextsContext.js +15 -0
- package/dist/toolkit/componentTextsContext.js.map +1 -0
- package/dist/toolkit/hashNavigation.js +40 -0
- package/dist/toolkit/hashNavigation.js.map +1 -0
- package/dist/toolkit/navigationPaths.js +49 -0
- package/dist/toolkit/navigationPaths.js.map +1 -0
- package/dist/toolkit/toolkitSettings.js +24 -4
- package/dist/toolkit/toolkitSettings.js.map +1 -1
- package/dist/toolkit/useFormatters.js +12 -0
- package/dist/toolkit/useFormatters.js.map +1 -0
- package/dist/toolkit/useToolkitTranslation.js +11 -7
- package/dist/toolkit/useToolkitTranslation.js.map +1 -1
- package/dist/types/DataTable/AuditTrailTable.d.ts +3 -1
- package/dist/types/DataTable/EmptyState.d.ts +7 -2
- package/dist/types/DataTable/FilterChips.d.ts +3 -1
- package/dist/types/DataTable/GoToPage.d.ts +7 -2
- package/dist/types/DataTable/SelectionBar.d.ts +32 -0
- package/dist/types/DataTable/ServerTable.d.ts +64 -3
- package/dist/types/DataTable/index.d.ts +1 -1
- package/dist/types/DataTable/rowSelection.d.ts +113 -0
- package/dist/types/access/PermissionMatrix.d.ts +3 -1
- package/dist/types/account/AccountSecuritySection.d.ts +3 -1
- package/dist/types/account/ForgotPasswordScreen.d.ts +3 -1
- package/dist/types/account/ProfileScreen.d.ts +31 -1
- package/dist/types/account/ResetPasswordScreen.d.ts +3 -1
- package/dist/types/account/SignInScreen.d.ts +3 -1
- package/dist/types/account/accountConnector.d.ts +7 -2
- package/dist/types/adapters/reactRouterRoutes.d.ts +14 -2
- package/dist/types/api/createApiClient.d.ts +46 -1
- package/dist/types/appShell/AppLayout.d.ts +15 -1
- package/dist/types/appShell/AppearanceSettings.d.ts +8 -1
- package/dist/types/appShell/BarActionButton.d.ts +2 -2
- package/dist/types/appShell/BarActions.d.ts +6 -1
- package/dist/types/appShell/BarCapsule.d.ts +8 -0
- package/dist/types/appShell/BarSearch.d.ts +24 -0
- package/dist/types/appShell/MenuLinks.d.ts +17 -0
- package/dist/types/appShell/MobileTabBar.d.ts +4 -1
- package/dist/types/appShell/SettingsMenu.d.ts +3 -1
- package/dist/types/appShell/SideMenu.d.ts +3 -1
- package/dist/types/appShell/Wordmark.d.ts +6 -0
- package/dist/types/appShell/menuItemText.d.ts +16 -0
- package/dist/types/appShell/shellSizes.d.ts +1 -0
- package/dist/types/auth/PasswordChecklist.d.ts +3 -1
- package/dist/types/calendar/EventCalendar.d.ts +18 -1
- package/dist/types/calendar/EventDetails.d.ts +36 -0
- package/dist/types/calendar/calendarEvents.d.ts +27 -0
- package/dist/types/calendar.d.ts +2 -0
- package/dist/types/charts.d.ts +4 -0
- package/dist/types/datePickers/DateInput.d.ts +3 -1
- package/dist/types/downloads/DownloadManager.d.ts +7 -1
- package/dist/types/downloads/downloadQueue.d.ts +8 -3
- package/dist/types/downloads/fileTypes.d.ts +39 -0
- package/dist/types/downloads/index.d.ts +1 -0
- package/dist/types/feedback/ConfirmDialog.d.ts +3 -1
- package/dist/types/feedback/ModalWaiting.d.ts +7 -2
- package/dist/types/feedback/NewBuildNotice.d.ts +3 -1
- package/dist/types/filepond/FileDropzone.d.ts +3 -1
- package/dist/types/formatting.d.ts +45 -90
- package/dist/types/forms/FormWizard.d.ts +3 -1
- package/dist/types/forms/auth/ChangePasswordForm.d.ts +3 -1
- package/dist/types/forms/auth/ForgotPasswordForm.d.ts +3 -1
- package/dist/types/forms/auth/SignInForm.d.ts +3 -1
- package/dist/types/forms/security/SecondStepForm.d.ts +3 -1
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +3 -1
- package/dist/types/hookForm/FormWizard.d.ts +22 -0
- package/dist/types/inbox/InboxLayout.d.ts +14 -2
- package/dist/types/inbox/MessageList.d.ts +30 -5
- package/dist/types/inbox/MessageView.d.ts +35 -3
- package/dist/types/inbox/inboxDates.d.ts +6 -1
- package/dist/types/index.d.ts +15 -8
- package/dist/types/inputs/CatalogInput.d.ts +9 -5
- package/dist/types/inputs/ColorInput.d.ts +3 -1
- package/dist/types/inputs/FormSection.d.ts +2 -2
- package/dist/types/inputs/NumberInput.d.ts +3 -1
- package/dist/types/inputs/SelectInput.d.ts +9 -5
- package/dist/types/inputs/TextInput.d.ts +3 -1
- package/dist/types/inputs/WizardLayout.d.ts +34 -0
- package/dist/types/kanban/KanbanBoard.d.ts +61 -6
- package/dist/types/kanban/kanbanCards.d.ts +22 -0
- package/dist/types/kanban.d.ts +1 -0
- package/dist/types/locales/en.d.ts +77 -0
- package/dist/types/locales/es.d.ts +77 -0
- package/dist/types/maps/MapView.d.ts +12 -3
- package/dist/types/navigation/appMenu.d.ts +8 -0
- package/dist/types/observability/logger.d.ts +13 -0
- package/dist/types/observability/usePublishedBuildWatch.d.ts +3 -1
- package/dist/types/pwa/InstallAppSetting.d.ts +7 -1
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +3 -1
- package/dist/types/pwa/InstallPromptBanner.d.ts +10 -1
- package/dist/types/pwa/useInstallPrompt.d.ts +3 -2
- package/dist/types/react-hook-form.d.ts +1 -0
- package/dist/types/security/ActivityLog.d.ts +3 -1
- package/dist/types/security/RecoveryCodesDialog.d.ts +6 -1
- package/dist/types/security/SessionsCard.d.ts +3 -1
- package/dist/types/table.d.ts +3 -1
- package/dist/types/theme/components/MuiAppBar.d.ts +3 -1
- package/dist/types/theme/components/MuiDialog.d.ts +9 -0
- package/dist/types/theme/components/glassComponents.d.ts +5 -0
- package/dist/types/theme/lightDarkStyles.d.ts +8 -0
- package/dist/types/toolkit/ComponentTexts.d.ts +11 -0
- package/dist/types/toolkit/ToolkitProvider.d.ts +18 -2
- package/dist/types/toolkit/browserNavigation.d.ts +23 -28
- package/dist/types/toolkit/componentTextsContext.d.ts +20 -0
- package/dist/types/toolkit/hashNavigation.d.ts +12 -0
- package/dist/types/toolkit/navigationPaths.d.ts +43 -0
- package/dist/types/toolkit/toolkitSettings.d.ts +49 -4
- package/dist/types/toolkit/useFormatters.d.ts +6 -0
- package/dist/types/toolkit/useToolkitTranslation.d.ts +2 -1
- package/dist/types/ui/actions/ActionsMenu.d.ts +20 -0
- package/dist/types/ui/actions/CopyButton.d.ts +3 -1
- package/dist/types/ui/actions/DownloadMenu.d.ts +8 -3
- package/dist/types/ui/actions/SecretField.d.ts +5 -1
- package/dist/types/ui/actions/SelectionActions.d.ts +39 -0
- package/dist/types/ui/filters/DateRangeFilter.d.ts +3 -1
- package/dist/types/ui/filters/QuickFilters.d.ts +3 -1
- package/dist/types/ui/index.d.ts +1 -1
- package/dist/types/ui/labels/tagOf.d.ts +13 -0
- package/dist/types/ui/layout/CollapsibleSection.d.ts +4 -1
- package/dist/types/ui/layout/ContentDialog.d.ts +3 -1
- package/dist/types/ui/layout/ErrorPage.d.ts +14 -1
- package/dist/types/ui/layout/FullPageLoader.d.ts +7 -1
- package/dist/types/ui/layout/PageHeading.d.ts +3 -1
- package/dist/types/ui/metrics/BarList.d.ts +7 -1
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +7 -1
- package/dist/types/ui/metrics/ChartTooltip.d.ts +9 -3
- package/dist/types/ui/metrics/ColumnChart.d.ts +23 -11
- package/dist/types/ui/metrics/LineChart.d.ts +41 -0
- package/dist/types/ui/metrics/MetricCard.d.ts +9 -1
- package/dist/types/ui/metrics/TableToggle.d.ts +9 -2
- package/dist/types/ui/metrics/chartParts.d.ts +112 -0
- package/dist/types/ui/navigation/BackButton.d.ts +3 -1
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +8 -2
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +7 -2
- package/dist/types/ui/navigation/LanguageToggle.d.ts +8 -3
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +5 -2
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +4 -1
- package/dist/types/upload/FileDropzone.d.ts +3 -1
- package/dist/ui/actions/ActionsMenu.js +51 -0
- package/dist/ui/actions/ActionsMenu.js.map +1 -0
- package/dist/ui/actions/CopyButton.js +2 -2
- package/dist/ui/actions/CopyButton.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +11 -12
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SecretField.js +8 -4
- package/dist/ui/actions/SecretField.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +142 -0
- package/dist/ui/actions/SelectionActions.js.map +1 -0
- package/dist/ui/filters/DateRangeFilter.js +4 -3
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +2 -2
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/labels/tagOf.js +7 -0
- package/dist/ui/labels/tagOf.js.map +1 -0
- package/dist/ui/layout/CollapsibleSection.js +6 -2
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +2 -2
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +21 -7
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +2 -2
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +2 -2
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/metrics/BarList.js +18 -13
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +11 -6
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js +1 -1
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +59 -63
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +117 -0
- package/dist/ui/metrics/LineChart.js.map +1 -0
- package/dist/ui/metrics/MetricCard.js +41 -8
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +4 -2
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +93 -0
- package/dist/ui/metrics/chartParts.js.map +1 -0
- package/dist/ui/navigation/BackButton.js +2 -2
- package/dist/ui/navigation/BackButton.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +6 -3
- 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 +40 -19
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +2 -2
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +10 -8
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/FileDropzone.js +5 -3
- package/dist/upload/FileDropzone.js.map +1 -1
- package/package.json +16 -7
package/dist/formatting.js
CHANGED
|
@@ -1,162 +1,189 @@
|
|
|
1
1
|
import { parseInstant, parsePlainDate } from "./dates.js";
|
|
2
|
-
import {
|
|
2
|
+
import { intlLocaleOf, toolkitSettings } from "./toolkit/toolkitSettings.js";
|
|
3
3
|
const formatterCache = /* @__PURE__ */ new Map();
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
function formattersOf(settingsOf) {
|
|
5
|
+
function cachedFormatter(kind, options, createFormatter) {
|
|
6
|
+
const settings = settingsOf();
|
|
7
|
+
const locale = intlLocaleOf(settings.language, settings.languages);
|
|
8
|
+
const cacheKey = `${kind}|${locale}|${JSON.stringify(options)}`;
|
|
9
|
+
if (!formatterCache.has(cacheKey)) {
|
|
10
|
+
formatterCache.set(cacheKey, createFormatter(locale, options));
|
|
11
|
+
}
|
|
12
|
+
return formatterCache.get(cacheKey);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
function formatNumber2(value, fallback = "—") {
|
|
15
|
+
if (value === null || value === void 0 || value === "" || Number.isNaN(Number(value))) {
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
18
|
+
const formatter = cachedFormatter(
|
|
19
|
+
"number",
|
|
20
|
+
{ maximumFractionDigits: 0 },
|
|
21
|
+
(locale, options) => new Intl.NumberFormat(locale, options)
|
|
22
|
+
);
|
|
23
|
+
return formatter.format(Number(value));
|
|
18
24
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function formatList(items) {
|
|
27
|
-
const formatter = cachedFormatter(
|
|
28
|
-
"list",
|
|
29
|
-
{ style: "long", type: "conjunction" },
|
|
30
|
-
(locale, options) => new Intl.ListFormat(locale, options)
|
|
31
|
-
);
|
|
32
|
-
return formatter.format(items);
|
|
33
|
-
}
|
|
34
|
-
function formatCurrency(value, fallback = "—") {
|
|
35
|
-
if (value === null || value === void 0 || value === "" || Number.isNaN(Number(value))) {
|
|
36
|
-
return fallback;
|
|
25
|
+
function formatList2(items) {
|
|
26
|
+
const formatter = cachedFormatter(
|
|
27
|
+
"list",
|
|
28
|
+
{ style: "long", type: "conjunction" },
|
|
29
|
+
(locale, options) => new Intl.ListFormat(locale, options)
|
|
30
|
+
);
|
|
31
|
+
return formatter.format(items);
|
|
37
32
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return
|
|
33
|
+
function formatCurrency2(value, fallback = "—") {
|
|
34
|
+
if (value === null || value === void 0 || value === "" || Number.isNaN(Number(value))) {
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
const formatter = cachedFormatter("currency", {
|
|
38
|
+
style: "currency",
|
|
39
|
+
currency: settingsOf().currency,
|
|
40
|
+
currencyDisplay: "narrowSymbol",
|
|
41
|
+
minimumFractionDigits: 2,
|
|
42
|
+
maximumFractionDigits: 2
|
|
43
|
+
}, (locale, options) => new Intl.NumberFormat(locale, options));
|
|
44
|
+
return formatter.format(Number(value));
|
|
50
45
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
function formatFileSize2(bytes, fallback = "—") {
|
|
47
|
+
if (bytes === null || bytes === void 0 || Number.isNaN(Number(bytes))) {
|
|
48
|
+
return fallback;
|
|
49
|
+
}
|
|
50
|
+
const units = [["gigabyte", 1e9], ["megabyte", 1e6], ["kilobyte", 1e3]];
|
|
51
|
+
for (const [unit, unitBytes] of units) {
|
|
52
|
+
if (Number(bytes) >= unitBytes) {
|
|
53
|
+
const formatter2 = cachedFormatter(
|
|
54
|
+
`fileSize-${unit}`,
|
|
55
|
+
{ style: "unit", unit, maximumFractionDigits: 1 },
|
|
56
|
+
(locale, options) => new Intl.NumberFormat(locale, options)
|
|
57
|
+
);
|
|
58
|
+
return formatter2.format(Number(bytes) / unitBytes);
|
|
59
|
+
}
|
|
60
60
|
}
|
|
61
|
+
const formatter = cachedFormatter(
|
|
62
|
+
"fileSize-byte",
|
|
63
|
+
{ style: "unit", unit: "byte", unitDisplay: "long", maximumFractionDigits: 0 },
|
|
64
|
+
(locale, options) => new Intl.NumberFormat(locale, options)
|
|
65
|
+
);
|
|
66
|
+
return formatter.format(Number(bytes));
|
|
61
67
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
function formatPercent2(value, { fractionDigits = 2, isRatio = false, fallback = "—" } = {}) {
|
|
69
|
+
if (value === null || value === void 0 || Number.isNaN(Number(value))) {
|
|
70
|
+
return fallback;
|
|
71
|
+
}
|
|
72
|
+
const ratio = isRatio ? Number(value) : Number(value) / 100;
|
|
73
|
+
const formatter = cachedFormatter(
|
|
74
|
+
"percent",
|
|
75
|
+
{ style: "percent", minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits },
|
|
76
|
+
(locale, options) => new Intl.NumberFormat(locale, options)
|
|
77
|
+
);
|
|
78
|
+
return formatter.format(ratio);
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
function formatDateTime2(value, { fallback = "—" } = {}) {
|
|
81
|
+
const instant = parseInstant(value);
|
|
82
|
+
if (!instant) {
|
|
83
|
+
return fallback;
|
|
84
|
+
}
|
|
85
|
+
const options = {
|
|
86
|
+
timeZone: settingsOf().timeZone,
|
|
87
|
+
day: "numeric",
|
|
88
|
+
month: "short",
|
|
89
|
+
year: "numeric",
|
|
90
|
+
hour: "2-digit",
|
|
91
|
+
minute: "2-digit",
|
|
92
|
+
hourCycle: "h23"
|
|
93
|
+
};
|
|
94
|
+
const formatter = cachedFormatter("dateTime", options, (locale, formatOptions) => new Intl.DateTimeFormat(locale, formatOptions));
|
|
95
|
+
return formatter.format(instant);
|
|
85
96
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
{ timeZone: "UTC", day: "numeric", month: "short", year: "numeric" },
|
|
97
|
+
function formatDate2(value, { fallback = "—" } = {}) {
|
|
98
|
+
const calendarDate = parsePlainDate(value);
|
|
99
|
+
if (calendarDate) {
|
|
100
|
+
const formatter2 = cachedFormatter(
|
|
101
|
+
"plainDate",
|
|
102
|
+
{ timeZone: "UTC", day: "numeric", month: "short", year: "numeric" },
|
|
103
|
+
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
104
|
+
);
|
|
105
|
+
return formatter2.format(new Date(Date.UTC(calendarDate.year, calendarDate.month - 1, calendarDate.day, 12)));
|
|
106
|
+
}
|
|
107
|
+
const instant = parseInstant(value);
|
|
108
|
+
if (!instant) {
|
|
109
|
+
return fallback;
|
|
110
|
+
}
|
|
111
|
+
const formatter = cachedFormatter(
|
|
112
|
+
"date",
|
|
113
|
+
{ timeZone: settingsOf().timeZone, day: "numeric", month: "short", year: "numeric" },
|
|
104
114
|
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
105
115
|
);
|
|
106
|
-
return
|
|
116
|
+
return formatter.format(instant);
|
|
107
117
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
118
|
+
function formatTime2(value, { fallback = "—" } = {}) {
|
|
119
|
+
const instant = parseInstant(value);
|
|
120
|
+
if (!instant) {
|
|
121
|
+
return fallback;
|
|
122
|
+
}
|
|
123
|
+
const formatter = cachedFormatter(
|
|
124
|
+
"time",
|
|
125
|
+
{ timeZone: settingsOf().timeZone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" },
|
|
126
|
+
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
127
|
+
);
|
|
128
|
+
return formatter.format(instant);
|
|
111
129
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
130
|
+
function formatMonthName2(year, month, { style = "long", capitalize = false } = {}) {
|
|
131
|
+
if (!Number.isInteger(Number(year)) || !Number.isInteger(Number(month))) {
|
|
132
|
+
return "—";
|
|
133
|
+
}
|
|
134
|
+
const formatter = cachedFormatter(
|
|
135
|
+
"monthName",
|
|
136
|
+
{ timeZone: "UTC", month: style },
|
|
137
|
+
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
138
|
+
);
|
|
139
|
+
const monthName = formatter.format(new Date(Date.UTC(Number(year), Number(month) - 1, 15, 12))).replace(/\.$/, "");
|
|
140
|
+
const label = `${monthName} ${year}`;
|
|
141
|
+
return capitalize ? label.charAt(0).toUpperCase() + label.slice(1) : label;
|
|
123
142
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
function formatMonthName(year, month, { style = "long", capitalize = false } = {}) {
|
|
132
|
-
if (!Number.isInteger(Number(year)) || !Number.isInteger(Number(month))) {
|
|
133
|
-
return "—";
|
|
143
|
+
function formatMonthShortName2(month) {
|
|
144
|
+
const formatter = cachedFormatter(
|
|
145
|
+
"monthShortName",
|
|
146
|
+
{ timeZone: "UTC", month: "short" },
|
|
147
|
+
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
148
|
+
);
|
|
149
|
+
return formatter.format(new Date(Date.UTC(2024, Number(month) - 1, 15, 12))).replace(/\.$/, "");
|
|
134
150
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
157
|
-
);
|
|
158
|
-
return formatter.format(new Date(Date.UTC(2024, 0, Number(isoWeekday), 12))).replace(/\.$/, "");
|
|
151
|
+
function formatWeekdayShortName2(isoWeekday) {
|
|
152
|
+
const formatter = cachedFormatter(
|
|
153
|
+
"weekdayShortName",
|
|
154
|
+
{ timeZone: "UTC", weekday: "short" },
|
|
155
|
+
(locale, options) => new Intl.DateTimeFormat(locale, options)
|
|
156
|
+
);
|
|
157
|
+
return formatter.format(new Date(Date.UTC(2024, 0, Number(isoWeekday), 12))).replace(/\.$/, "");
|
|
158
|
+
}
|
|
159
|
+
return Object.freeze({
|
|
160
|
+
formatNumber: formatNumber2,
|
|
161
|
+
formatList: formatList2,
|
|
162
|
+
formatCurrency: formatCurrency2,
|
|
163
|
+
formatFileSize: formatFileSize2,
|
|
164
|
+
formatPercent: formatPercent2,
|
|
165
|
+
formatDateTime: formatDateTime2,
|
|
166
|
+
formatDate: formatDate2,
|
|
167
|
+
formatTime: formatTime2,
|
|
168
|
+
formatMonthName: formatMonthName2,
|
|
169
|
+
formatMonthShortName: formatMonthShortName2,
|
|
170
|
+
formatWeekdayShortName: formatWeekdayShortName2
|
|
171
|
+
});
|
|
159
172
|
}
|
|
173
|
+
const currentFormatters = formattersOf(toolkitSettings);
|
|
174
|
+
const {
|
|
175
|
+
formatNumber,
|
|
176
|
+
formatList,
|
|
177
|
+
formatCurrency,
|
|
178
|
+
formatFileSize,
|
|
179
|
+
formatPercent,
|
|
180
|
+
formatDateTime,
|
|
181
|
+
formatDate,
|
|
182
|
+
formatTime,
|
|
183
|
+
formatMonthName,
|
|
184
|
+
formatMonthShortName,
|
|
185
|
+
formatWeekdayShortName
|
|
186
|
+
} = currentFormatters;
|
|
160
187
|
function dayOfPlainDate(value) {
|
|
161
188
|
return parsePlainDate(value)?.day ?? null;
|
|
162
189
|
}
|
|
@@ -172,6 +199,7 @@ export {
|
|
|
172
199
|
formatNumber,
|
|
173
200
|
formatPercent,
|
|
174
201
|
formatTime,
|
|
175
|
-
formatWeekdayShortName
|
|
202
|
+
formatWeekdayShortName,
|
|
203
|
+
formattersOf
|
|
176
204
|
};
|
|
177
205
|
//# sourceMappingURL=formatting.js.map
|
package/dist/formatting.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatting.js","sources":["../src/formatting.js"],"sourcesContent":["// Cómo se escriben números, porcentajes y fechas en pantalla.\n//\n// Siempre en el idioma activo (es-MX o en-US), las fechas en la zona y los montos en la moneda del proveedor\n// (ToolkitProvider). Cuando una hora necesita rótulo, la pantalla lo escribe con su propio texto («hora del centro de\n// México») y no con la abreviatura de Intl (GMT-6), que no le dice nada a la persona.\n//\n// Los Intl.* se memorizan por idioma: crear un formateador por celda en una tabla de cientos de filas se nota.\n//\n// Funciones puras, no renderizan: function (regla de la casa).\nimport {parseInstant, parsePlainDate} from '@/dates.js';\nimport {intlLocaleOf, toolkitSettings} from '@/toolkit/toolkitSettings.js';\n\nconst formatterCache = new Map();\n\nfunction activeLocale() {\n return intlLocaleOf(toolkitSettings().language);\n}\n\nfunction cachedFormatter(kind, options, createFormatter) {\n const locale = activeLocale();\n const cacheKey = `${kind}|${locale}|${JSON.stringify(options)}`;\n if (!formatterCache.has(cacheKey)) {\n formatterCache.set(cacheKey, createFormatter(locale, options));\n }\n return formatterCache.get(cacheKey);\n}\n\n/**\n * Número con separador de miles: 321,540.\n *\n * @param {number|string|null|undefined} value\n * @param {string} [fallback] lo que se pinta si no hay dato\n */\nexport function formatNumber(value, fallback = '—') {\n if (value === null || value === undefined || value === '' || Number.isNaN(Number(value))) {\n return fallback;\n }\n const formatter = cachedFormatter('number', {maximumFractionDigits: 0},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(value));\n}\n\n/**\n * Una lista en una frase, con su «y» antes de la última parte: «resumen, motivos de rechazo y descargas».\n *\n * @param {string[]} items las partes de la lista, ya traducidas\n * @returns {string} la frase; '' si la lista viene vacía\n */\nexport function formatList(items) {\n const formatter = cachedFormatter('list', {style: 'long', type: 'conjunction'},\n (locale, options) => new Intl.ListFormat(locale, options));\n return formatter.format(items);\n}\n\n\n/**\n * Monto en la moneda del proveedor con dos decimales: $2,567,951.00. Recibe el texto que manda el API (numeric de la base) o un número;\n * solo pinta, nunca suma (las sumas las hace el backend al centavo).\n *\n * @param {number|string|null|undefined} value\n * @param {string} [fallback] lo que se pinta si no hay dato\n */\nexport function formatCurrency(value, fallback = '—') {\n if (value === null || value === undefined || value === '' || Number.isNaN(Number(value))) {\n return fallback;\n }\n const formatter = cachedFormatter('currency', {\n style: 'currency', currency: toolkitSettings().currency, currencyDisplay: 'narrowSymbol', minimumFractionDigits: 2, maximumFractionDigits: 2,\n }, (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(value));\n}\n\n/**\n * Tamaño de un archivo en la unidad que se lee mejor: 950 B, 12.4 kB, 60.1 MB, 2.3 GB.\n *\n * @param {number|null|undefined} bytes\n * @param {string} [fallback] lo que se pinta si no hay dato\n */\nexport function formatFileSize(bytes, fallback = '—') {\n if (bytes === null || bytes === undefined || Number.isNaN(Number(bytes))) {\n return fallback;\n }\n // unidades de Intl en base 1000, igual que las que enseña el navegador en sus descargas\n const units = [['gigabyte', 1e9], ['megabyte', 1e6], ['kilobyte', 1e3]];\n for (const [unit, unitBytes] of units) {\n if (Number(bytes) >= unitBytes) {\n const formatter = cachedFormatter(`fileSize-${unit}`, {style: 'unit', unit, maximumFractionDigits: 1},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(bytes) / unitBytes);\n }\n }\n // OJO: con la forma corta Intl no pone plural («74 byte», y «1 B» con el uno); la larga dice «74 bytes» y «1 byte»,\n // como lo enseña la Mac. Las demás unidades se quedan cortas (kB, MB): ahí la abreviatura sí se lee bien.\n const formatter = cachedFormatter('fileSize-byte', {style: 'unit', unit: 'byte', unitDisplay: 'long', maximumFractionDigits: 0},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(bytes));\n}\n\n/**\n * Porcentaje a partir de una proporción o de un número ya en porcentaje.\n *\n * @param {number|null|undefined} value 99.58 (con isRatio false) o 0.9958 (con isRatio true)\n * @param {{fractionDigits?: number, isRatio?: boolean, fallback?: string}} [options]\n */\nexport function formatPercent(value, {fractionDigits = 2, isRatio = false, fallback = '—'} = {}) {\n if (value === null || value === undefined || Number.isNaN(Number(value))) {\n return fallback;\n }\n const ratio = isRatio ? Number(value) : Number(value) / 100;\n const formatter = cachedFormatter('percent', {style: 'percent', minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(ratio);\n}\n\n/**\n * Fecha y hora de un instante ISO en la zona del negocio.\n *\n * @param {string|null|undefined} value instante ISO con desfase\n * @param {{fallback?: string}} [options]\n */\nexport function formatDateTime(value, {fallback = '—'} = {}) {\n const instant = parseInstant(value);\n if (!instant) {\n return fallback;\n }\n const options = {\n timeZone: toolkitSettings().timeZone,\n day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', hourCycle: 'h23',\n };\n const formatter = cachedFormatter('dateTime', options, (locale, formatOptions) => new Intl.DateTimeFormat(locale, formatOptions));\n return formatter.format(instant);\n}\n\n/**\n * Solo la fecha. Acepta un instante ISO (se pasa a la zona del negocio) o una fecha de calendario \"2026-08-16\"\n * (se pinta tal cual, sin moverla de día).\n *\n * @param {string|null|undefined} value\n * @param {{fallback?: string}} [options]\n */\nexport function formatDate(value, {fallback = '—'} = {}) {\n const calendarDate = parsePlainDate(value);\n if (calendarDate) {\n const formatter = cachedFormatter('plainDate', {timeZone: 'UTC', day: 'numeric', month: 'short', year: 'numeric'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // mediodía UTC y Intl en UTC: el mismo día en cualquier zona\n return formatter.format(new Date(Date.UTC(calendarDate.year, calendarDate.month - 1, calendarDate.day, 12)));\n }\n const instant = parseInstant(value);\n if (!instant) {\n return fallback;\n }\n const formatter = cachedFormatter('date', {timeZone: toolkitSettings().timeZone, day: 'numeric', month: 'short', year: 'numeric'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n return formatter.format(instant);\n}\n\n/**\n * Solo la hora (24 h) de un instante ISO en la zona del negocio: \"07:00\".\n *\n * Existe aparte de formatDateTime porque las pantallas escriben \"16 ago 2026 · 07:00\" con punto medio, y Intl une\n * fecha y hora con coma: se arma formatDate + formatTime.\n *\n * @param {string|null|undefined} value instante ISO con desfase\n * @param {{fallback?: string}} [options]\n */\nexport function formatTime(value, {fallback = '—'} = {}) {\n const instant = parseInstant(value);\n if (!instant) {\n return fallback;\n }\n const formatter = cachedFormatter('time', {timeZone: toolkitSettings().timeZone, hour: '2-digit', minute: '2-digit', hourCycle: 'h23'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n return formatter.format(instant);\n}\n\n/**\n * Nombre de un mes de calendario: \"agosto 2026\" (long) o \"ago 2026\" (short).\n *\n * OJO: no se usa Intl con {month, year} juntos porque en español sale \"agosto de 2026\" y las pantallas dicen\n * \"agosto 2026\"; se pide solo el mes y se pega el año.\n *\n * @param {number} year\n * @param {number} month 1 a 12\n * @param {{style?: 'long'|'short', capitalize?: boolean}} [options] capitalize: \"Agosto 2026\" (fila de mes)\n */\nexport function formatMonthName(year, month, {style = 'long', capitalize = false} = {}) {\n if (!Number.isInteger(Number(year)) || !Number.isInteger(Number(month))) {\n return '—';\n }\n const formatter = cachedFormatter('monthName', {timeZone: 'UTC', month: style},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // día 15 a mediodía UTC: el mismo mes en cualquier zona\n const monthName = formatter.format(new Date(Date.UTC(Number(year), Number(month) - 1, 15, 12))).replace(/\\.$/, '');\n const label = `${monthName} ${year}`;\n return capitalize ? label.charAt(0).toUpperCase() + label.slice(1) : label;\n}\n\n/**\n * Nombre corto de un mes sin año: \"ene\", \"feb\" (rótulos de columnas de calendario y rejillas por periodo).\n *\n * @param {number} month 1 a 12\n * @returns {string}\n */\nexport function formatMonthShortName(month) {\n const formatter = cachedFormatter('monthShortName', {timeZone: 'UTC', month: 'short'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // 2024 empieza en lunes y es bisiesto: da igual, solo se pide el mes a mediodía UTC\n return formatter.format(new Date(Date.UTC(2024, Number(month) - 1, 15, 12))).replace(/\\.$/, '');\n}\n\n/**\n * Nombre corto de un día de la semana: \"lun\", \"mié\" (renglones del calendario de actividad).\n *\n * @param {number} isoWeekday 1 = lunes ... 7 = domingo\n * @returns {string}\n */\nexport function formatWeekdayShortName(isoWeekday) {\n const formatter = cachedFormatter('weekdayShortName', {timeZone: 'UTC', weekday: 'short'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // el 1 de enero de 2024 fue lunes: lunes + (isoWeekday - 1) días\n return formatter.format(new Date(Date.UTC(2024, 0, Number(isoWeekday), 12))).replace(/\\.$/, '');\n}\n\n/**\n * Día del mes de una fecha de calendario (\"2026-08-16\" → 16), sin moverla de zona.\n *\n * @param {string|null|undefined} value\n * @returns {number|null}\n */\nexport function dayOfPlainDate(value) {\n return parsePlainDate(value)?.day ?? null;\n}\n"],"names":["formatter"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"formatting.js","sources":["../src/formatting.js"],"sourcesContent":["// Cómo se escriben números, porcentajes y fechas en pantalla.\n//\n// Siempre en el idioma activo (es-MX o en-US), las fechas en la zona y los montos en la moneda del proveedor\n// (ToolkitProvider). Cuando una hora necesita rótulo, la pantalla lo escribe con su propio texto («hora del centro de\n// México») y no con la abreviatura de Intl (GMT-6), que no le dice nada a la persona.\n//\n// Los Intl.* se memorizan por idioma: crear un formateador por celda en una tabla de cientos de filas se nota.\n//\n// Dos formas de usarlos:\n// - sueltos (formatDate, formatNumber…): con la configuración vigente, la del ToolkitProvider de la raíz. Para lo que no\n// es componente (un aviso, un Excel);\n// - dentro de un componente, useFormatters() (toolkit/useFormatters.js): con la del proveedor MÁS CERCANO. Con dos\n// proveedores (una parte de la app en otro idioma, o dos islas de React en una página vieja) cada parte escribe con\n// el suyo; con los sueltos, el último proveedor que se pintaba le cambiaba el idioma a todos.\n// Los dos salen de formattersOf: una sola versión de cada formato.\n//\n// Funciones puras, no renderizan: function (regla de la casa).\nimport {parseInstant, parsePlainDate} from '@/dates.js';\nimport {intlLocaleOf, toolkitSettings} from '@/toolkit/toolkitSettings.js';\n\nconst formatterCache = new Map();\n\n/**\n * Los formatos con la configuración que da settingsOf (se lee en cada llamada: si cambia el idioma, el siguiente formato ya\n * sale en el nuevo).\n *\n * @param {() => {language: string, languages?: Array<{code: string, locale?: string}>, timeZone: string, currency: string}} settingsOf\n */\nexport function formattersOf(settingsOf) {\n function cachedFormatter(kind, options, createFormatter) {\n const settings = settingsOf();\n const locale = intlLocaleOf(settings.language, settings.languages);\n const cacheKey = `${kind}|${locale}|${JSON.stringify(options)}`;\n if (!formatterCache.has(cacheKey)) {\n formatterCache.set(cacheKey, createFormatter(locale, options));\n }\n return formatterCache.get(cacheKey);\n }\n\n /**\n * Número con separador de miles: 321,540.\n *\n * @param {number|string|null|undefined} value\n * @param {string} [fallback] lo que se pinta si no hay dato\n * @returns {string}\n */\n function formatNumber(value, fallback = '—') {\n if (value === null || value === undefined || value === '' || Number.isNaN(Number(value))) {\n return fallback;\n }\n const formatter = cachedFormatter('number', {maximumFractionDigits: 0},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(value));\n }\n\n /**\n * Una lista en una frase, con su «y» antes de la última parte: «resumen, motivos de rechazo y descargas».\n *\n * @param {string[]} items las partes de la lista, ya traducidas\n * @returns {string} la frase; '' si la lista viene vacía\n */\n function formatList(items) {\n const formatter = cachedFormatter('list', {style: 'long', type: 'conjunction'},\n (locale, options) => new Intl.ListFormat(locale, options));\n return formatter.format(items);\n }\n\n\n /**\n * Monto en la moneda del proveedor con dos decimales: $2,567,951.00. Recibe el texto que manda el API (numeric de la base) o un número;\n * solo pinta, nunca suma (las sumas las hace el backend al centavo).\n *\n * @param {number|string|null|undefined} value\n * @param {string} [fallback] lo que se pinta si no hay dato\n * @returns {string}\n */\n function formatCurrency(value, fallback = '—') {\n if (value === null || value === undefined || value === '' || Number.isNaN(Number(value))) {\n return fallback;\n }\n const formatter = cachedFormatter('currency', {\n style: 'currency', currency: settingsOf().currency, currencyDisplay: 'narrowSymbol', minimumFractionDigits: 2, maximumFractionDigits: 2,\n }, (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(value));\n }\n\n /**\n * Tamaño de un archivo en la unidad que se lee mejor: 950 B, 12.4 kB, 60.1 MB, 2.3 GB.\n *\n * @param {number|null|undefined} bytes\n * @param {string} [fallback] lo que se pinta si no hay dato\n * @returns {string}\n */\n function formatFileSize(bytes, fallback = '—') {\n if (bytes === null || bytes === undefined || Number.isNaN(Number(bytes))) {\n return fallback;\n }\n // unidades de Intl en base 1000, igual que las que enseña el navegador en sus descargas\n const units = [['gigabyte', 1e9], ['megabyte', 1e6], ['kilobyte', 1e3]];\n for (const [unit, unitBytes] of units) {\n if (Number(bytes) >= unitBytes) {\n const formatter = cachedFormatter(`fileSize-${unit}`, {style: 'unit', unit, maximumFractionDigits: 1},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(bytes) / unitBytes);\n }\n }\n // OJO: con la forma corta Intl no pone plural («74 byte», y «1 B» con el uno); la larga dice «74 bytes» y «1 byte»,\n // como lo enseña la Mac. Las demás unidades se quedan cortas (kB, MB): ahí la abreviatura sí se lee bien.\n const formatter = cachedFormatter('fileSize-byte', {style: 'unit', unit: 'byte', unitDisplay: 'long', maximumFractionDigits: 0},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(Number(bytes));\n }\n\n /**\n * Porcentaje a partir de una proporción o de un número ya en porcentaje.\n *\n * @param {number|null|undefined} value 99.58 (con isRatio false) o 0.9958 (con isRatio true)\n * @param {{fractionDigits?: number, isRatio?: boolean, fallback?: string}} [options]\n * @returns {string}\n */\n function formatPercent(value, {fractionDigits = 2, isRatio = false, fallback = '—'} = {}) {\n if (value === null || value === undefined || Number.isNaN(Number(value))) {\n return fallback;\n }\n const ratio = isRatio ? Number(value) : Number(value) / 100;\n const formatter = cachedFormatter('percent', {style: 'percent', minimumFractionDigits: fractionDigits, maximumFractionDigits: fractionDigits},\n (locale, options) => new Intl.NumberFormat(locale, options));\n return formatter.format(ratio);\n }\n\n /**\n * Fecha y hora de un instante ISO en la zona del negocio.\n *\n * @param {string|null|undefined} value instante ISO con desfase\n * @param {{fallback?: string}} [options]\n * @returns {string}\n */\n function formatDateTime(value, {fallback = '—'} = {}) {\n const instant = parseInstant(value);\n if (!instant) {\n return fallback;\n }\n const options = {\n timeZone: settingsOf().timeZone,\n day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', hourCycle: 'h23',\n };\n const formatter = cachedFormatter('dateTime', options, (locale, formatOptions) => new Intl.DateTimeFormat(locale, formatOptions));\n return formatter.format(instant);\n }\n\n /**\n * Solo la fecha. Acepta un instante ISO (se pasa a la zona del negocio) o una fecha de calendario \"2026-08-16\"\n * (se pinta tal cual, sin moverla de día).\n *\n * @param {string|null|undefined} value\n * @param {{fallback?: string}} [options]\n * @returns {string}\n */\n function formatDate(value, {fallback = '—'} = {}) {\n const calendarDate = parsePlainDate(value);\n if (calendarDate) {\n const formatter = cachedFormatter('plainDate', {timeZone: 'UTC', day: 'numeric', month: 'short', year: 'numeric'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // mediodía UTC y Intl en UTC: el mismo día en cualquier zona\n return formatter.format(new Date(Date.UTC(calendarDate.year, calendarDate.month - 1, calendarDate.day, 12)));\n }\n const instant = parseInstant(value);\n if (!instant) {\n return fallback;\n }\n const formatter = cachedFormatter('date', {timeZone: settingsOf().timeZone, day: 'numeric', month: 'short', year: 'numeric'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n return formatter.format(instant);\n }\n\n /**\n * Solo la hora (24 h) de un instante ISO en la zona del negocio: \"07:00\".\n *\n * Existe aparte de formatDateTime porque las pantallas escriben \"16 ago 2026 · 07:00\" con punto medio, y Intl une\n * fecha y hora con coma: se arma formatDate + formatTime.\n *\n * @param {string|null|undefined} value instante ISO con desfase\n * @param {{fallback?: string}} [options]\n * @returns {string}\n */\n function formatTime(value, {fallback = '—'} = {}) {\n const instant = parseInstant(value);\n if (!instant) {\n return fallback;\n }\n const formatter = cachedFormatter('time', {timeZone: settingsOf().timeZone, hour: '2-digit', minute: '2-digit', hourCycle: 'h23'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n return formatter.format(instant);\n }\n\n /**\n * Nombre de un mes de calendario: \"agosto 2026\" (long) o \"ago 2026\" (short).\n *\n * OJO: no se usa Intl con {month, year} juntos porque en español sale \"agosto de 2026\" y las pantallas dicen\n * \"agosto 2026\"; se pide solo el mes y se pega el año.\n *\n * @param {number} year\n * @param {number} month 1 a 12\n * @param {{style?: 'long'|'short', capitalize?: boolean}} [options] capitalize: \"Agosto 2026\" (fila de mes)\n */\n function formatMonthName(year, month, {style = 'long', capitalize = false} = {}) {\n if (!Number.isInteger(Number(year)) || !Number.isInteger(Number(month))) {\n return '—';\n }\n const formatter = cachedFormatter('monthName', {timeZone: 'UTC', month: style},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // día 15 a mediodía UTC: el mismo mes en cualquier zona\n const monthName = formatter.format(new Date(Date.UTC(Number(year), Number(month) - 1, 15, 12))).replace(/\\.$/, '');\n const label = `${monthName} ${year}`;\n return capitalize ? label.charAt(0).toUpperCase() + label.slice(1) : label;\n }\n\n /**\n * Nombre corto de un mes sin año: \"ene\", \"feb\" (rótulos de columnas de calendario y rejillas por periodo).\n *\n * @param {number} month 1 a 12\n * @returns {string}\n */\n function formatMonthShortName(month) {\n const formatter = cachedFormatter('monthShortName', {timeZone: 'UTC', month: 'short'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // 2024 empieza en lunes y es bisiesto: da igual, solo se pide el mes a mediodía UTC\n return formatter.format(new Date(Date.UTC(2024, Number(month) - 1, 15, 12))).replace(/\\.$/, '');\n }\n\n /**\n * Nombre corto de un día de la semana: \"lun\", \"mié\" (renglones del calendario de actividad).\n *\n * @param {number} isoWeekday 1 = lunes ... 7 = domingo\n * @returns {string}\n */\n function formatWeekdayShortName(isoWeekday) {\n const formatter = cachedFormatter('weekdayShortName', {timeZone: 'UTC', weekday: 'short'},\n (locale, options) => new Intl.DateTimeFormat(locale, options));\n // el 1 de enero de 2024 fue lunes: lunes + (isoWeekday - 1) días\n return formatter.format(new Date(Date.UTC(2024, 0, Number(isoWeekday), 12))).replace(/\\.$/, '');\n }\n\n return Object.freeze({\n formatNumber, formatList, formatCurrency, formatFileSize, formatPercent, formatDateTime, formatDate, formatTime, formatMonthName,\n formatMonthShortName, formatWeekdayShortName,\n });\n}\n\n// los sueltos: con la configuración vigente (la del proveedor de la raíz)\nconst currentFormatters = formattersOf(toolkitSettings);\nexport const {\n formatNumber, formatList, formatCurrency, formatFileSize, formatPercent, formatDateTime, formatDate, formatTime, formatMonthName,\n formatMonthShortName, formatWeekdayShortName,\n} = currentFormatters;\n\n/**\n * Día del mes de una fecha de calendario (\"2026-08-16\" → 16), sin moverla de zona.\n *\n * @param {string|null|undefined} value\n * @returns {number|null}\n */\nexport function dayOfPlainDate(value) {\n return parsePlainDate(value)?.day ?? null;\n}\n"],"names":["formatNumber","formatList","formatCurrency","formatFileSize","formatter","formatPercent","formatDateTime","formatDate","formatTime","formatMonthName","formatMonthShortName","formatWeekdayShortName"],"mappings":"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"}
|
package/dist/forms/FormWizard.js
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useRef, useEffect } from "react";
|
|
4
4
|
import { useFormikContext, getIn } from "formik";
|
|
5
|
-
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
6
|
-
import Box from "@mui/material/Box";
|
|
7
|
-
import Button from "@mui/material/Button";
|
|
8
|
-
import Stack from "@mui/material/Stack";
|
|
9
|
-
import Step from "@mui/material/Step";
|
|
10
|
-
import StepLabel from "@mui/material/StepLabel";
|
|
11
|
-
import Stepper from "@mui/material/Stepper";
|
|
12
|
-
import Typography from "@mui/material/Typography";
|
|
13
5
|
import FormProblemAlert from "./FormProblemAlert.js";
|
|
14
6
|
import SubmitButton from "./SubmitButton.js";
|
|
7
|
+
import WizardLayout from "../inputs/WizardLayout.js";
|
|
15
8
|
function firstStepWithErrorOf(steps, errors) {
|
|
16
9
|
return steps.findIndex((step) => (step.fields ?? []).some((field) => getIn(errors, field)));
|
|
17
10
|
}
|
|
18
|
-
function FormWizard({ steps, submitLabel }) {
|
|
19
|
-
const { t } = useToolkitTranslation("common");
|
|
11
|
+
function FormWizard({ steps, submitLabel, texts }) {
|
|
20
12
|
const { validateForm, setFieldTouched, setFieldValue, initialValues, errors, isSubmitting } = useFormikContext();
|
|
21
13
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
22
14
|
const [skippedKeys, setSkippedKeys] = useState(() => /* @__PURE__ */ new Set());
|
|
23
15
|
const wasSubmitting = useRef(false);
|
|
24
16
|
const activeStep = steps[activeIndex];
|
|
25
|
-
const isLastStep = activeIndex === steps.length - 1;
|
|
26
17
|
useEffect(() => {
|
|
27
18
|
if (wasSubmitting.current && !isSubmitting) {
|
|
28
19
|
const errorStepIndex = firstStepWithErrorOf(steps, errors);
|
|
@@ -50,39 +41,21 @@ function FormWizard({ steps, submitLabel }) {
|
|
|
50
41
|
setSkippedKeys((currentKeys) => /* @__PURE__ */ new Set([...currentKeys, activeStep.key]));
|
|
51
42
|
setActiveIndex((currentIndex) => currentIndex + 1);
|
|
52
43
|
};
|
|
53
|
-
return /* @__PURE__ */
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
/* @__PURE__ */ jsx(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
isLastStep && /* @__PURE__ */ jsx(FormProblemAlert, {}),
|
|
69
|
-
/* @__PURE__ */ jsxs(Stack, { direction: { xs: "column-reverse", sm: "row" }, spacing: 1.5, sx: { justifyContent: "space-between" }, children: [
|
|
70
|
-
/* @__PURE__ */ jsx(
|
|
71
|
-
Button,
|
|
72
|
-
{
|
|
73
|
-
variant: "text",
|
|
74
|
-
color: "inherit",
|
|
75
|
-
disabled: activeIndex === 0 || isSubmitting,
|
|
76
|
-
onClick: () => setActiveIndex((currentIndex) => currentIndex - 1),
|
|
77
|
-
children: t("wizard.back")
|
|
78
|
-
}
|
|
79
|
-
),
|
|
80
|
-
/* @__PURE__ */ jsxs(Stack, { direction: { xs: "column-reverse", sm: "row" }, spacing: 1.5, children: [
|
|
81
|
-
activeStep.optional && !isLastStep && /* @__PURE__ */ jsx(Button, { variant: "text", onClick: skipStep, children: t("wizard.skip") }),
|
|
82
|
-
isLastStep ? /* @__PURE__ */ jsx(SubmitButton, { children: submitLabel }) : /* @__PURE__ */ jsx(Button, { variant: "contained", size: "large", onClick: goNext, children: t("wizard.next") })
|
|
83
|
-
] })
|
|
84
|
-
] })
|
|
85
|
-
] });
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
WizardLayout,
|
|
46
|
+
{
|
|
47
|
+
steps,
|
|
48
|
+
activeIndex,
|
|
49
|
+
skippedKeys,
|
|
50
|
+
isSubmitting,
|
|
51
|
+
onBack: () => setActiveIndex((currentIndex) => currentIndex - 1),
|
|
52
|
+
onNext: goNext,
|
|
53
|
+
onSkip: skipStep,
|
|
54
|
+
submitButton: /* @__PURE__ */ jsx(SubmitButton, { children: submitLabel }),
|
|
55
|
+
problemAlert: /* @__PURE__ */ jsx(FormProblemAlert, {}),
|
|
56
|
+
texts
|
|
57
|
+
}
|
|
58
|
+
);
|
|
86
59
|
}
|
|
87
60
|
export {
|
|
88
61
|
FormWizard as default
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormWizard.js","sources":["../../src/forms/FormWizard.jsx"],"sourcesContent":["// Formulario por pasos (wizard) con formik: un solo formulario partido en pasos, con pasos opcionales que se pueden omitir.\n//\n// Cómo se porta, para que la pantalla solo declare sus pasos:\n// - «Siguiente» revisa SOLO los campos del paso (fields) y no avanza si alguno tiene error; los marca como tocados para\n// que se vea por qué;\n// - «Omitir este paso» (solo en los opcionales) regresa sus campos a como empezaron: omitir es no dar ese dato, no dejar\n// lo que se escribió a medias;\n// - el último paso envía TODO el formulario (el esquema de yup completo vale para todos los pasos);\n// - si el envío falla por un campo de un paso anterior (yup o el 422 del servidor con applyServerProblem), el wizard\n// regresa solo a ese paso: un error que no se ve no se puede corregir.\n//\n// Va DENTRO de <Formik> y <Form>:\n// <Formik initialValues={…} validationSchema={…} onSubmit={…}>\n// <Form noValidate>\n// <FormWizard submitLabel=\"Enviar solicitud\" steps={[\n// {key: 'datos', label: 'Sus datos', fields: ['name', 'email'], content: <>…campos…</>},\n// {key: 'aval', label: 'Aval', optional: true, fields: ['guarantor'], content: <>…</>},\n// ]} />\n// </Form>\n// </Formik>\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useRef, useState} from 'react';\nimport {getIn, useFormikContext} from 'formik';\nimport
|
|
1
|
+
{"version":3,"file":"FormWizard.js","sources":["../../src/forms/FormWizard.jsx"],"sourcesContent":["// Formulario por pasos (wizard) con formik: un solo formulario partido en pasos, con pasos opcionales que se pueden omitir.\n//\n// Cómo se porta, para que la pantalla solo declare sus pasos:\n// - «Siguiente» revisa SOLO los campos del paso (fields) y no avanza si alguno tiene error; los marca como tocados para\n// que se vea por qué;\n// - «Omitir este paso» (solo en los opcionales) regresa sus campos a como empezaron: omitir es no dar ese dato, no dejar\n// lo que se escribió a medias;\n// - el último paso envía TODO el formulario (el esquema de yup completo vale para todos los pasos);\n// - si el envío falla por un campo de un paso anterior (yup o el 422 del servidor con applyServerProblem), el wizard\n// regresa solo a ese paso: un error que no se ve no se puede corregir.\n//\n// Va DENTRO de <Formik> y <Form>:\n// <Formik initialValues={…} validationSchema={…} onSubmit={…}>\n// <Form noValidate>\n// <FormWizard submitLabel=\"Enviar solicitud\" steps={[\n// {key: 'datos', label: 'Sus datos', fields: ['name', 'email'], content: <>…campos…</>},\n// {key: 'aval', label: 'Aval', optional: true, fields: ['guarantor'], content: <>…</>},\n// ]} />\n// </Form>\n// </Formik>\n//\n// Lo que se ve vive en WizardLayout (inputs/), el mismo del FormWizard de react-hook-form. Los textos («Siguiente»,\n// «Anterior»…) se cambian con `texts`: {wizard: {next: 'Continuar'}}.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useRef, useState} from 'react';\nimport {getIn, useFormikContext} from 'formik';\n\nimport FormProblemAlert from '@/forms/FormProblemAlert.jsx';\nimport SubmitButton from '@/forms/SubmitButton.jsx';\nimport WizardLayout from '@/inputs/WizardLayout.jsx';\n\n/**\n * El primer paso que tiene un campo con error; -1 si ninguno.\n *\n * @param {Array<{fields?: string[]}>} steps\n * @param {object} errors los de formik\n * @returns {number}\n */\nfunction firstStepWithErrorOf(steps, errors) {\n return steps.findIndex((step) => (step.fields ?? []).some((field) => getIn(errors, field)));\n}\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {string[]} [props.steps[].fields] los campos del paso (los que revisa «Siguiente»)\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {string} props.submitLabel ya traducido: el botón del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function FormWizard({steps, submitLabel, texts}) {\n const {validateForm, setFieldTouched, setFieldValue, initialValues, errors, isSubmitting} = useFormikContext();\n const [activeIndex, setActiveIndex] = useState(0);\n const [skippedKeys, setSkippedKeys] = useState(() => new Set());\n const wasSubmitting = useRef(false);\n const activeStep = steps[activeIndex];\n\n // al terminar un envío fallido, regresa al paso con el primer error (solo en ese momento: escribir en otro paso no\n // debe jalar a la persona de regreso)\n useEffect(() => {\n if (wasSubmitting.current && !isSubmitting) {\n const errorStepIndex = firstStepWithErrorOf(steps, errors);\n if (errorStepIndex >= 0) {\n setActiveIndex(errorStepIndex);\n }\n }\n wasSubmitting.current = isSubmitting;\n }, [isSubmitting, errors, steps]);\n\n const goNext = async () => {\n const stepFields = activeStep.fields ?? [];\n const formErrors = await validateForm();\n stepFields.forEach((field) => setFieldTouched(field, true, false));\n // guard clause: el paso tiene errores; se queda para que se corrijan\n if (stepFields.some((field) => getIn(formErrors, field))) {\n return;\n }\n setSkippedKeys((currentKeys) => new Set([...currentKeys].filter((key) => key !== activeStep.key)));\n setActiveIndex((currentIndex) => currentIndex + 1);\n };\n\n const skipStep = () => {\n for (const field of activeStep.fields ?? []) {\n setFieldValue(field, getIn(initialValues, field), false);\n setFieldTouched(field, false, false);\n }\n setSkippedKeys((currentKeys) => new Set([...currentKeys, activeStep.key]));\n setActiveIndex((currentIndex) => currentIndex + 1);\n };\n\n return (\n <WizardLayout steps={steps} activeIndex={activeIndex} skippedKeys={skippedKeys} isSubmitting={isSubmitting}\n onBack={() => setActiveIndex((currentIndex) => currentIndex - 1)} onNext={goNext} onSkip={skipStep}\n submitButton={<SubmitButton>{submitLabel}</SubmitButton>} problemAlert={<FormProblemAlert />} texts={texts} />\n );\n}\n"],"names":[],"mappings":";;;;;;;AAuCA,SAAS,qBAAqB,OAAO,QAAQ;AACzC,SAAO,MAAM,UAAU,CAAC,UAAU,KAAK,UAAU,CAAA,GAAI,KAAK,CAAC,UAAU,MAAM,QAAQ,KAAK,CAAC,CAAC;AAC9F;AAaA,SAAwB,WAAW,EAAC,OAAO,aAAa,SAAQ;AAC5D,QAAM,EAAC,cAAc,iBAAiB,eAAe,eAAe,QAAQ,aAAA,IAAgB,iBAAA;AAC5F,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,CAAC;AAChD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,MAAM,oBAAI,KAAK;AAC9D,QAAM,gBAAgB,OAAO,KAAK;AAClC,QAAM,aAAa,MAAM,WAAW;AAIpC,YAAU,MAAM;AACZ,QAAI,cAAc,WAAW,CAAC,cAAc;AACxC,YAAM,iBAAiB,qBAAqB,OAAO,MAAM;AACzD,UAAI,kBAAkB,GAAG;AACrB,uBAAe,cAAc;AAAA,MACjC;AAAA,IACJ;AACA,kBAAc,UAAU;AAAA,EAC5B,GAAG,CAAC,cAAc,QAAQ,KAAK,CAAC;AAEhC,QAAM,SAAS,YAAY;AACvB,UAAM,aAAa,WAAW,UAAU,CAAA;AACxC,UAAM,aAAa,MAAM,aAAA;AACzB,eAAW,QAAQ,CAAC,UAAU,gBAAgB,OAAO,MAAM,KAAK,CAAC;AAEjE,QAAI,WAAW,KAAK,CAAC,UAAU,MAAM,YAAY,KAAK,CAAC,GAAG;AACtD;AAAA,IACJ;AACA,mBAAe,CAAC,gBAAgB,IAAI,IAAI,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,QAAQ,QAAQ,WAAW,GAAG,CAAC,CAAC;AACjG,mBAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,EACrD;AAEA,QAAM,WAAW,MAAM;AACnB,eAAW,SAAS,WAAW,UAAU,CAAA,GAAI;AACzC,oBAAc,OAAO,MAAM,eAAe,KAAK,GAAG,KAAK;AACvD,sBAAgB,OAAO,OAAO,KAAK;AAAA,IACvC;AACA,mBAAe,CAAC,gBAAgB,oBAAI,IAAI,CAAC,GAAG,aAAa,WAAW,GAAG,CAAC,CAAC;AACzE,mBAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,EACrD;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAa;AAAA,MAAc;AAAA,MAA0B;AAAA,MAA0B;AAAA,MAClE,QAAQ,MAAM,eAAe,CAAC,iBAAiB,eAAe,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAQ,QAAQ;AAAA,MAC1F,cAAc,oBAAC,cAAA,EAAc,UAAA,YAAA,CAAY;AAAA,MAAiB,kCAAe,kBAAA,EAAiB;AAAA,MAAI;AAAA,IAAA;AAAA,EAAA;AAEpH;"}
|
|
@@ -12,8 +12,9 @@ import FormTextField from "../FormTextField.js";
|
|
|
12
12
|
import SubmitButton from "../SubmitButton.js";
|
|
13
13
|
import { applyServerProblem } from "../applyServerProblem.js";
|
|
14
14
|
import * as Yup from "yup";
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import ComponentTexts from "../../toolkit/ComponentTexts.js";
|
|
16
|
+
function ChangePasswordForm({ onSubmit, requireCurrentPassword = true, policy = DEFAULT_PASSWORD_POLICY, texts }) {
|
|
17
|
+
const { t } = useToolkitTranslation("auth", texts);
|
|
17
18
|
const [isDone, setIsDone] = useState(false);
|
|
18
19
|
const schema = useMemo(() => Yup.object({
|
|
19
20
|
currentPassword: requireCurrentPassword ? Yup.string().required() : Yup.string(),
|
|
@@ -30,7 +31,7 @@ function ChangePasswordForm({ onSubmit, requireCurrentPassword = true, policy =
|
|
|
30
31
|
applyServerProblem(error, { ...formikHelpers, values });
|
|
31
32
|
}
|
|
32
33
|
};
|
|
33
|
-
return /* @__PURE__ */ jsx(Formik, { initialValues: { currentPassword: "", newPassword: "", confirmPassword: "" }, validationSchema: schema, onSubmit: submit, children: ({ values }) => /* @__PURE__ */ jsx(Form, { noValidate: true, children: /* @__PURE__ */ jsxs(Stack, { spacing: 2, children: [
|
|
34
|
+
return /* @__PURE__ */ jsx(ComponentTexts, { namespace: "auth", texts, children: /* @__PURE__ */ jsx(Formik, { initialValues: { currentPassword: "", newPassword: "", confirmPassword: "" }, validationSchema: schema, onSubmit: submit, children: ({ values }) => /* @__PURE__ */ jsx(Form, { noValidate: true, children: /* @__PURE__ */ jsxs(Stack, { spacing: 2, children: [
|
|
34
35
|
isDone && /* @__PURE__ */ jsx(Alert, { severity: "success", role: "status", children: t("changePassword.done") }),
|
|
35
36
|
/* @__PURE__ */ jsx(FormProblemAlert, {}),
|
|
36
37
|
requireCurrentPassword && /* @__PURE__ */ jsx(FormTextField, { name: "currentPassword", label: t("changePassword.current"), type: "password", autoComplete: "current-password" }),
|
|
@@ -38,7 +39,7 @@ function ChangePasswordForm({ onSubmit, requireCurrentPassword = true, policy =
|
|
|
38
39
|
/* @__PURE__ */ jsx(PasswordChecklist, { password: values.newPassword, policy }),
|
|
39
40
|
/* @__PURE__ */ jsx(FormTextField, { name: "confirmPassword", label: t("changePassword.confirm"), type: "password", autoComplete: "new-password" }),
|
|
40
41
|
/* @__PURE__ */ jsx(SubmitButton, { sx: { alignSelf: "flex-start" }, children: t("changePassword.submit") })
|
|
41
|
-
] }) }) });
|
|
42
|
+
] }) }) }) });
|
|
42
43
|
}
|
|
43
44
|
export {
|
|
44
45
|
ChangePasswordForm as default
|