@stackcraftmx/calcita 0.23.0 → 0.25.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 +111 -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 +35 -5
- package/dist/api/createApiClient.js.map +1 -1
- package/dist/appShell/AppLayout.js +27 -7
- 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/MobileTabBar.js +3 -1
- package/dist/appShell/MobileTabBar.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 +157 -133
- 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/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/MuiDialog.js +13 -0
- package/dist/theme/components/MuiDialog.js.map +1 -0
- package/dist/theme/createToolkitTheme.js.map +1 -1
- 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 +29 -1
- package/dist/types/appShell/AppLayout.d.ts +11 -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/MobileTabBar.d.ts +4 -1
- 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 +12 -5
- 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/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/MuiDialog.d.ts +9 -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 +14 -6
|
@@ -6,6 +6,7 @@ import Paper from "@mui/material/Paper";
|
|
|
6
6
|
import Stack from "@mui/material/Stack";
|
|
7
7
|
import Typography from "@mui/material/Typography";
|
|
8
8
|
import { GLASS_PANEL_RADIUS_PIXELS, isGlassTheme, ambientBackgroundOf, glassStylesOf } from "../theme/glassSurface.js";
|
|
9
|
+
import NewBuildNotice, { NEW_BUILD_NOTICE_VARIANT } from "../feedback/NewBuildNotice.js";
|
|
9
10
|
import GlassLensFilter from "../ui/layout/GlassLensFilter.js";
|
|
10
11
|
import DocumentTitle from "../ui/navigation/DocumentTitle.js";
|
|
11
12
|
import ColorSchemeToggle from "../ui/navigation/ColorSchemeToggle.js";
|
|
@@ -20,6 +21,7 @@ const AuthLayout = ({ logo, brandName, title, description, children, footer, sho
|
|
|
20
21
|
], children: [
|
|
21
22
|
/* @__PURE__ */ jsx(DocumentTitle, { title }),
|
|
22
23
|
/* @__PURE__ */ jsx(GlassLensFilter, {}),
|
|
24
|
+
/* @__PURE__ */ jsx(NewBuildNotice, { variant: NEW_BUILD_NOTICE_VARIANT.floating }),
|
|
23
25
|
/* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { justifyContent: "flex-end", p: 2, pt: "max(16px, env(safe-area-inset-top))" }, children: [
|
|
24
26
|
showLanguageToggle && /* @__PURE__ */ jsx(LanguageToggle, {}),
|
|
25
27
|
showColorSchemeToggle && /* @__PURE__ */ jsx(ColorSchemeToggle, {})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthLayout.js","sources":["../../src/appShell/AuthLayout.jsx"],"sourcesContent":["// La pantalla de entrada (iniciar sesión, «¿olvidó su contraseña?», elegir una contraseña nueva): el logo y el nombre de\n// la app, una tarjeta al centro con su título y lo que lleve adentro, y arriba a la derecha el idioma y claro u oscuro\n// (quien no ha entrado también los necesita). Como la plantilla «Sign in» de MUI.\n//\n// <AuthLayout logo={<Logo />} title={t('auth:signIn.title')} description={t('auth:signIn.subtitle')}>\n// <SignInForm onSubmit={signIn} forgotPasswordPath=\"/forgot-password\" />\n// </AuthLayout>\n//\n// Con el vidrio del tema (createToolkitTheme({glass: true})), el lienzo lleva sus manchas de color y la tarjeta es de\n// vidrio; sin vidrio, una tarjeta con borde sobre el lienzo.\n//\n// Lo renderiza la app: arrow (regla de la casa).\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport Box from '@mui/material/Box';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {GLASS_PANEL_RADIUS_PIXELS, ambientBackgroundOf, glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport GlassLensFilter from '@/ui/layout/GlassLensFilter.jsx';\nimport DocumentTitle from '@/ui/navigation/DocumentTitle.jsx';\nimport ColorSchemeToggle from '@/ui/navigation/ColorSchemeToggle.jsx';\nimport LanguageToggle from '@/ui/navigation/LanguageToggle.jsx';\n\n// lo ancho de la tarjeta: lo que cabe cómodo un correo largo\nconst CARD_MAX_WIDTH_PIXELS = 440;\n\n/**\n * La tarjeta: de vidrio si el tema lo trae; si no, con borde.\n *\n * @param {object} theme\n * @returns {object}\n */\nconst cardSurfaceOf = (theme) => (isGlassTheme(theme)\n ? {...glassStylesOf(theme), borderColor: 'transparent', borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`}\n : {});\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} [props.logo] el logo chico (28 px)\n * @param {string} [props.brandName] por omisión el appName de ToolkitProvider\n * @param {string} props.title el título de la tarjeta y de la pestaña, ya traducido\n * @param {import('react').ReactNode} [props.description] el renglón debajo del título\n * @param {import('react').ReactNode} props.children el formulario\n * @param {import('react').ReactNode} [props.footer] debajo de la tarjeta (los botones de prueba, el aviso de privacidad…)\n * @param {boolean} [props.showLanguageToggle] por omisión sí\n * @param {boolean} [props.showColorSchemeToggle] por omisión sí\n */\nexport const AuthLayout = ({logo, brandName, title, description, children, footer, showLanguageToggle = true, showColorSchemeToggle = true}) => {\n const {appName} = useToolkitSettings();\n return (\n <Box sx={[{minHeight: '100vh', display: 'flex', flexDirection: 'column', bgcolor: 'background.default'},\n (theme) => (isGlassTheme(theme) ? ambientBackgroundOf(theme) : {})]}>\n <DocumentTitle title={title} />\n <GlassLensFilter />\n <Stack direction=\"row\" spacing={1} sx={{justifyContent: 'flex-end', p: 2, pt: 'max(16px, env(safe-area-inset-top))'}}>\n {showLanguageToggle && <LanguageToggle />}\n {showColorSchemeToggle && <ColorSchemeToggle />}\n </Stack>\n <Box component=\"main\" sx={{flexGrow: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', px: 2, pb: 6}}>\n <Stack direction=\"row\" spacing={1.25} sx={{alignItems: 'center', mb: 3}}>\n {logo && <Box sx={{width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center'}}>{logo}</Box>}\n <Typography variant=\"subtitle1\" component=\"p\" sx={{fontWeight: 600}}>{brandName ?? appName}</Typography>\n </Stack>\n <Paper variant=\"outlined\" sx={[{width: '100%', maxWidth: CARD_MAX_WIDTH_PIXELS, p: {xs: 3, sm: 4}}, cardSurfaceOf]}>\n <Typography variant=\"h5\" component=\"h1\">{title}</Typography>\n {description && <Typography variant=\"body2\" color=\"text.secondary\" sx={{mt: 1}}>{description}</Typography>}\n <Box sx={{mt: 3}}>{children}</Box>\n </Paper>\n {footer && <Box sx={{width: '100%', maxWidth: CARD_MAX_WIDTH_PIXELS, mt: 2}}>{footer}</Box>}\n </Box>\n </Box>\n );\n};\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AuthLayout.js","sources":["../../src/appShell/AuthLayout.jsx"],"sourcesContent":["// La pantalla de entrada (iniciar sesión, «¿olvidó su contraseña?», elegir una contraseña nueva): el logo y el nombre de\n// la app, una tarjeta al centro con su título y lo que lleve adentro, y arriba a la derecha el idioma y claro u oscuro\n// (quien no ha entrado también los necesita). Como la plantilla «Sign in» de MUI.\n//\n// <AuthLayout logo={<Logo />} title={t('auth:signIn.title')} description={t('auth:signIn.subtitle')}>\n// <SignInForm onSubmit={signIn} forgotPasswordPath=\"/forgot-password\" />\n// </AuthLayout>\n//\n// Con el vidrio del tema (createToolkitTheme({glass: true})), el lienzo lleva sus manchas de color y la tarjeta es de\n// vidrio; sin vidrio, una tarjeta con borde sobre el lienzo.\n//\n// También pinta el aviso de versión nueva (cápsula flotante), como el marco: quien abre la app sin sesión (o la deja abierta\n// en esta pantalla) también debe enterarse; antes solo salía en las pantallas con sesión.\n//\n// Lo renderiza la app: arrow (regla de la casa).\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport Box from '@mui/material/Box';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\n\nimport {GLASS_PANEL_RADIUS_PIXELS, ambientBackgroundOf, glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport NewBuildNotice, {NEW_BUILD_NOTICE_VARIANT} from '@/feedback/NewBuildNotice.jsx';\nimport GlassLensFilter from '@/ui/layout/GlassLensFilter.jsx';\nimport DocumentTitle from '@/ui/navigation/DocumentTitle.jsx';\nimport ColorSchemeToggle from '@/ui/navigation/ColorSchemeToggle.jsx';\nimport LanguageToggle from '@/ui/navigation/LanguageToggle.jsx';\n\n// lo ancho de la tarjeta: lo que cabe cómodo un correo largo\nconst CARD_MAX_WIDTH_PIXELS = 440;\n\n/**\n * La tarjeta: de vidrio si el tema lo trae; si no, con borde.\n *\n * @param {object} theme\n * @returns {object}\n */\nconst cardSurfaceOf = (theme) => (isGlassTheme(theme)\n ? {...glassStylesOf(theme), borderColor: 'transparent', borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`}\n : {});\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} [props.logo] el logo chico (28 px)\n * @param {string} [props.brandName] por omisión el appName de ToolkitProvider\n * @param {string} props.title el título de la tarjeta y de la pestaña, ya traducido\n * @param {import('react').ReactNode} [props.description] el renglón debajo del título\n * @param {import('react').ReactNode} props.children el formulario\n * @param {import('react').ReactNode} [props.footer] debajo de la tarjeta (los botones de prueba, el aviso de privacidad…)\n * @param {boolean} [props.showLanguageToggle] por omisión sí\n * @param {boolean} [props.showColorSchemeToggle] por omisión sí\n */\nexport const AuthLayout = ({logo, brandName, title, description, children, footer, showLanguageToggle = true, showColorSchemeToggle = true}) => {\n const {appName} = useToolkitSettings();\n return (\n <Box sx={[{minHeight: '100vh', display: 'flex', flexDirection: 'column', bgcolor: 'background.default'},\n (theme) => (isGlassTheme(theme) ? ambientBackgroundOf(theme) : {})]}>\n <DocumentTitle title={title} />\n <GlassLensFilter />\n <NewBuildNotice variant={NEW_BUILD_NOTICE_VARIANT.floating} />\n <Stack direction=\"row\" spacing={1} sx={{justifyContent: 'flex-end', p: 2, pt: 'max(16px, env(safe-area-inset-top))'}}>\n {showLanguageToggle && <LanguageToggle />}\n {showColorSchemeToggle && <ColorSchemeToggle />}\n </Stack>\n <Box component=\"main\" sx={{flexGrow: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', px: 2, pb: 6}}>\n <Stack direction=\"row\" spacing={1.25} sx={{alignItems: 'center', mb: 3}}>\n {logo && <Box sx={{width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center'}}>{logo}</Box>}\n <Typography variant=\"subtitle1\" component=\"p\" sx={{fontWeight: 600}}>{brandName ?? appName}</Typography>\n </Stack>\n <Paper variant=\"outlined\" sx={[{width: '100%', maxWidth: CARD_MAX_WIDTH_PIXELS, p: {xs: 3, sm: 4}}, cardSurfaceOf]}>\n <Typography variant=\"h5\" component=\"h1\">{title}</Typography>\n {description && <Typography variant=\"body2\" color=\"text.secondary\" sx={{mt: 1}}>{description}</Typography>}\n <Box sx={{mt: 3}}>{children}</Box>\n </Paper>\n {footer && <Box sx={{width: '100%', maxWidth: CARD_MAX_WIDTH_PIXELS, mt: 2}}>{footer}</Box>}\n </Box>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;AA6BA,MAAM,wBAAwB;AAQ9B,MAAM,gBAAgB,CAAC,UAAW,aAAa,KAAK,IAC9C,EAAC,GAAG,cAAc,KAAK,GAAG,aAAa,eAAe,cAAc,GAAG,yBAAyB,KAAA,IAChG,CAAA;AAaC,MAAM,aAAa,CAAC,EAAC,MAAM,WAAW,OAAO,aAAa,UAAU,QAAQ,qBAAqB,MAAM,wBAAwB,WAAU;AAC5I,QAAM,EAAC,QAAA,IAAW,mBAAA;AAClB,SACI,qBAAC,OAAI,IAAI;AAAA,IAAC,EAAC,WAAW,SAAS,SAAS,QAAQ,eAAe,UAAU,SAAS,qBAAA;AAAA,IAC9E,CAAC,UAAW,aAAa,KAAK,IAAI,oBAAoB,KAAK,IAAI,CAAA;AAAA,EAAC,GAChE,UAAA;AAAA,IAAA,oBAAC,iBAAc,OAAc;AAAA,wBAC5B,iBAAA,EAAgB;AAAA,IACjB,oBAAC,gBAAA,EAAe,SAAS,yBAAyB,SAAA,CAAU;AAAA,IAC5D,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,gBAAgB,YAAY,GAAG,GAAG,IAAI,yCACzE,UAAA;AAAA,MAAA,0CAAuB,gBAAA,EAAe;AAAA,MACtC,6CAA0B,mBAAA,CAAA,CAAkB;AAAA,IAAA,GACjD;AAAA,IACA,qBAAC,OAAI,WAAU,QAAO,IAAI,EAAC,UAAU,GAAG,SAAS,QAAQ,eAAe,UAAU,YAAY,UAAU,gBAAgB,UAAU,IAAI,GAAG,IAAI,KACzI,UAAA;AAAA,MAAA,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,MAAM,IAAI,EAAC,YAAY,UAAU,IAAI,EAAA,GAChE,UAAA;AAAA,QAAA,QAAQ,oBAAC,KAAA,EAAI,IAAI,EAAC,OAAO,IAAI,QAAQ,IAAI,SAAS,QAAQ,YAAY,UAAU,gBAAgB,YAAY,UAAA,MAAK;AAAA,QAClH,oBAAC,YAAA,EAAW,SAAQ,aAAY,WAAU,KAAI,IAAI,EAAC,YAAY,IAAA,GAAO,UAAA,aAAa,QAAA,CAAQ;AAAA,MAAA,GAC/F;AAAA,MACA,qBAAC,SAAM,SAAQ,YAAW,IAAI,CAAC,EAAC,OAAO,QAAQ,UAAU,uBAAuB,GAAG,EAAC,IAAI,GAAG,IAAI,IAAC,GAAI,aAAa,GAC7G,UAAA;AAAA,QAAA,oBAAC,YAAA,EAAW,SAAQ,MAAK,WAAU,MAAM,UAAA,OAAM;AAAA,QAC9C,eAAe,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,IAAI,EAAC,IAAI,EAAA,GAAK,UAAA,YAAA,CAAY;AAAA,4BAC5F,KAAA,EAAI,IAAI,EAAC,IAAI,EAAA,GAAK,SAAA,CAAS;AAAA,MAAA,GAChC;AAAA,MACC,UAAU,oBAAC,KAAA,EAAI,IAAI,EAAC,OAAO,QAAQ,UAAU,uBAAuB,IAAI,EAAA,GAAK,UAAA,OAAA,CAAO;AAAA,IAAA,EAAA,CACzF;AAAA,EAAA,GACJ;AAER;"}
|
|
@@ -16,11 +16,11 @@ function BarActionButton({ label, icon, onClick, disabled, color = "primary", ..
|
|
|
16
16
|
onClick?.(event);
|
|
17
17
|
};
|
|
18
18
|
return /* @__PURE__ */ jsxs(MenuItem, { onClick: pickFromMenu, disabled, ...buttonProps, children: [
|
|
19
|
-
/* @__PURE__ */ jsx(ListItemIcon, { children: icon }),
|
|
20
|
-
/* @__PURE__ */ jsx(ListItemText, { children: label })
|
|
19
|
+
icon && /* @__PURE__ */ jsx(ListItemIcon, { children: icon }),
|
|
20
|
+
/* @__PURE__ */ jsx(ListItemText, { inset: !icon, children: label })
|
|
21
21
|
] });
|
|
22
22
|
}
|
|
23
|
-
if (placement === BAR_ACTIONS_PLACEMENT.capsule) {
|
|
23
|
+
if (placement === BAR_ACTIONS_PLACEMENT.capsule && icon) {
|
|
24
24
|
return /* @__PURE__ */ jsx(Tooltip, { title: label, children: /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(IconButton, { "aria-label": label, onClick, disabled, color, ...buttonProps, children: icon }) }) });
|
|
25
25
|
}
|
|
26
26
|
return /* @__PURE__ */ jsx(Button, { startIcon: icon, onClick, disabled, color, ...buttonProps, children: label });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarActionButton.js","sources":["../../src/appShell/BarActionButton.jsx"],"sourcesContent":["// Un botón de acción de la pantalla que se acomoda solo a donde quedó (lo pidió el autor, y es lo que pide la guía de Apple\n// para las barras: «Prefer simple, recognizable symbols for items instead of text»):\n// - en una cápsula (arriba en la barra, flotando en el teléfono o en la franja): solo el ícono, con su nombre en el tooltip\n// y para el lector de pantalla. En el teléfono el tooltip sale al dejar el dedo encima, como en iOS;\n// - en el menú «⋯» del teléfono (las que ya no caben en el renglón flotante): un renglón con ícono y texto;\n// - suelto (fuera de BarActions): ícono con texto, botón sin relleno.\n// Lo demás va al botón (disabled, color, component y to para una liga…).\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\n\nimport {BAR_ACTIONS_PLACEMENT, useBarActionsPlacement, useCloseBarActionsMenu} from '@/appShell/barActionsSlot.js';\n\n/**\n * @param {object} props\n * @param {string} props.label el nombre de la acción, ya traducido: «Agregar sucursal»\n * @param {import('react').ReactNode} props.icon el ícono (<AddRoundedIcon />)\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.disabled]\n * @param {'inherit'|'primary'|'secondary'|'success'|'error'|'info'|'warning'} [props.color] por omisión primary\n */\nexport default function BarActionButton({label, icon, onClick, disabled, color = 'primary', ...buttonProps}) {\n const placement = useBarActionsPlacement();\n const closeMenu = useCloseBarActionsMenu();\n\n if (placement === BAR_ACTIONS_PLACEMENT.menu) {\n // primero se cierra el menú: la acción puede abrir una ventana y el menú quedaba encima\n const pickFromMenu = (event) => {\n closeMenu();\n onClick?.(event);\n };\n return (\n <MenuItem onClick={pickFromMenu} disabled={disabled} {...buttonProps}>\n <ListItemIcon>{icon}</ListItemIcon
|
|
1
|
+
{"version":3,"file":"BarActionButton.js","sources":["../../src/appShell/BarActionButton.jsx"],"sourcesContent":["// Un botón de acción de la pantalla que se acomoda solo a donde quedó (lo pidió el autor, y es lo que pide la guía de Apple\n// para las barras: «Prefer simple, recognizable symbols for items instead of text»):\n// - en una cápsula (arriba en la barra, flotando en el teléfono o en la franja): solo el ícono, con su nombre en el tooltip\n// y para el lector de pantalla. En el teléfono el tooltip sale al dejar el dedo encima, como en iOS;\n// - en el menú «⋯» del teléfono (las que ya no caben en el renglón flotante): un renglón con ícono y texto;\n// - suelto (fuera de BarActions): ícono con texto, botón sin relleno.\n// Lo demás va al botón (disabled, color, component y to para una liga…). Sin ícono, en la cápsula va su texto (un botón sin\n// relleno) y en el «⋯» el renglón sin ícono: mejor un texto que un botón vacío.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Button from '@mui/material/Button';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\n\nimport {BAR_ACTIONS_PLACEMENT, useBarActionsPlacement, useCloseBarActionsMenu} from '@/appShell/barActionsSlot.js';\n\n/**\n * @param {object} props\n * @param {string} props.label el nombre de la acción, ya traducido: «Agregar sucursal»\n * @param {import('react').ReactNode} [props.icon] el ícono (<AddRoundedIcon />); sin él, se ve su texto\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.disabled]\n * @param {'inherit'|'primary'|'secondary'|'success'|'error'|'info'|'warning'} [props.color] por omisión primary\n */\nexport default function BarActionButton({label, icon, onClick, disabled, color = 'primary', ...buttonProps}) {\n const placement = useBarActionsPlacement();\n const closeMenu = useCloseBarActionsMenu();\n\n if (placement === BAR_ACTIONS_PLACEMENT.menu) {\n // primero se cierra el menú: la acción puede abrir una ventana y el menú quedaba encima\n const pickFromMenu = (event) => {\n closeMenu();\n onClick?.(event);\n };\n return (\n <MenuItem onClick={pickFromMenu} disabled={disabled} {...buttonProps}>\n {icon && <ListItemIcon>{icon}</ListItemIcon>}\n <ListItemText inset={!icon}>{label}</ListItemText>\n </MenuItem>\n );\n }\n if (placement === BAR_ACTIONS_PLACEMENT.capsule && icon) {\n // OJO: el tooltip envuelve un span para que también salga con el botón deshabilitado (MUI no lo pinta sobre un\n // elemento deshabilitado)\n return (\n <Tooltip title={label}>\n <span>\n <IconButton aria-label={label} onClick={onClick} disabled={disabled} color={color} {...buttonProps}>{icon}</IconButton>\n </span>\n </Tooltip>\n );\n }\n return <Button startIcon={icon} onClick={onClick} disabled={disabled} color={color} {...buttonProps}>{label}</Button>;\n}\n"],"names":[],"mappings":";;;;;;;;;AA2BA,SAAwB,gBAAgB,EAAC,OAAO,MAAM,SAAS,UAAU,QAAQ,WAAW,GAAG,eAAc;AACzG,QAAM,YAAY,uBAAA;AAClB,QAAM,YAAY,uBAAA;AAElB,MAAI,cAAc,sBAAsB,MAAM;AAE1C,UAAM,eAAe,CAAC,UAAU;AAC5B,gBAAA;AACA,gBAAU,KAAK;AAAA,IACnB;AACA,gCACK,UAAA,EAAS,SAAS,cAAc,UAAqB,GAAG,aACpD,UAAA;AAAA,MAAA,QAAQ,oBAAC,gBAAc,UAAA,KAAA,CAAK;AAAA,MAC7B,oBAAC,cAAA,EAAa,OAAO,CAAC,MAAO,UAAA,MAAA,CAAM;AAAA,IAAA,GACvC;AAAA,EAER;AACA,MAAI,cAAc,sBAAsB,WAAW,MAAM;AAGrD,+BACK,SAAA,EAAQ,OAAO,OACZ,UAAA,oBAAC,UACG,UAAA,oBAAC,YAAA,EAAW,cAAY,OAAO,SAAkB,UAAoB,OAAe,GAAG,aAAc,UAAA,MAAK,GAC9G,EAAA,CACJ;AAAA,EAER;AACA,SAAO,oBAAC,UAAO,WAAW,MAAM,SAAkB,UAAoB,OAAe,GAAG,aAAc,UAAA,MAAA,CAAM;AAChH;"}
|
|
@@ -3,16 +3,19 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState, Children } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
import Box from "@mui/material/Box";
|
|
6
|
+
import GlobalStyles from "@mui/material/GlobalStyles";
|
|
6
7
|
import IconButton from "@mui/material/IconButton";
|
|
7
8
|
import Menu from "@mui/material/Menu";
|
|
8
9
|
import Tooltip from "@mui/material/Tooltip";
|
|
9
10
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
10
11
|
import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
|
|
11
|
-
import BarCapsule from "./BarCapsule.js";
|
|
12
|
+
import BarCapsule, { monochromeButtonsSxOf } from "./BarCapsule.js";
|
|
12
13
|
import { useBarActionsSlot, BarActionsPlacementContext, BAR_ACTIONS_PLACEMENT, BarActionsMenuCloseContext } from "./barActionsSlot.js";
|
|
13
14
|
import { DESKTOP_MEDIA_QUERY } from "./shellSizes.js";
|
|
14
15
|
import useFittingActionCount from "./useFittingActionCount.js";
|
|
15
16
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
17
|
+
const EXCLUSIVE_ATTRIBUTE = "data-bar-exclusive";
|
|
18
|
+
const HIDE_NOT_EXCLUSIVE_STYLES = { [`[data-phone-bar-actions] > :not([${EXCLUSIVE_ATTRIBUTE}])`]: { display: "none !important" } };
|
|
16
19
|
const InCapsule = ({ children }) => /* @__PURE__ */ jsx(BarActionsPlacementContext.Provider, { value: BAR_ACTIONS_PLACEMENT.capsule, children });
|
|
17
20
|
const MoreActionsMenu = ({ label, children }) => {
|
|
18
21
|
const [anchorElement, setAnchorElement] = useState(null);
|
|
@@ -42,18 +45,18 @@ const MoreActionsMenu = ({ label, children }) => {
|
|
|
42
45
|
)
|
|
43
46
|
] });
|
|
44
47
|
};
|
|
45
|
-
const PhoneFloatingCapsule = ({ phoneSlot, label, sx, children }) => {
|
|
46
|
-
const { t } = useToolkitTranslation("layout");
|
|
48
|
+
const PhoneFloatingCapsule = ({ phoneSlot, label, sx, texts, children, ...rest }) => {
|
|
49
|
+
const { t } = useToolkitTranslation("layout", texts);
|
|
47
50
|
const [capsuleElement, setCapsuleElement] = useState(null);
|
|
48
51
|
const actions = Children.toArray(children);
|
|
49
52
|
const visibleCount = useFittingActionCount(phoneSlot, capsuleElement, actions.length);
|
|
50
53
|
const hiddenActions = actions.slice(visibleCount);
|
|
51
|
-
return /* @__PURE__ */ jsxs(BarCapsule, { ref: setCapsuleElement, "aria-label": label, sx, children: [
|
|
54
|
+
return /* @__PURE__ */ jsxs(BarCapsule, { ref: setCapsuleElement, "aria-label": label, sx, ...rest, children: [
|
|
52
55
|
/* @__PURE__ */ jsx(InCapsule, { children: actions.slice(0, visibleCount) }),
|
|
53
56
|
hiddenActions.length > 0 && /* @__PURE__ */ jsx(MoreActionsMenu, { label: t("barActions.more"), children: hiddenActions })
|
|
54
57
|
] });
|
|
55
58
|
};
|
|
56
|
-
function BarActions({ children, label, order, sx, inlineSx, bare = false }) {
|
|
59
|
+
function BarActions({ children, label, order, sx, inlineSx, bare = false, exclusive = false, texts }) {
|
|
57
60
|
const { slot, phoneSlot, usesCapsules } = useBarActionsSlot();
|
|
58
61
|
const isDesktop = useMediaQuery(DESKTOP_MEDIA_QUERY);
|
|
59
62
|
const target = isDesktop ? slot : phoneSlot;
|
|
@@ -66,14 +69,22 @@ function BarActions({ children, label, order, sx, inlineSx, bare = false }) {
|
|
|
66
69
|
}
|
|
67
70
|
return /* @__PURE__ */ jsx(BarCapsule, { "aria-label": label, sx: [{ alignSelf: "flex-start" }, ...extraInlineSx], children: /* @__PURE__ */ jsx(InCapsule, { children }) });
|
|
68
71
|
}
|
|
72
|
+
const exclusiveProps = !isDesktop && exclusive ? { [EXCLUSIVE_ATTRIBUTE]: "" } : {};
|
|
73
|
+
const hideOthers = !isDesktop && exclusive && /* @__PURE__ */ jsx(GlobalStyles, { styles: HIDE_NOT_EXCLUSIVE_STYLES });
|
|
69
74
|
if (!isDesktop && !bare) {
|
|
70
|
-
return
|
|
75
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
76
|
+
hideOthers,
|
|
77
|
+
createPortal(/* @__PURE__ */ jsx(PhoneFloatingCapsule, { phoneSlot, label, sx: [orderSx, ...extraSx], texts, ...exclusiveProps, children }), target)
|
|
78
|
+
] });
|
|
71
79
|
}
|
|
72
80
|
if (usesCapsules && !bare) {
|
|
73
81
|
return createPortal(/* @__PURE__ */ jsx(BarCapsule, { "aria-label": label, sx: [orderSx, ...extraSx], children: /* @__PURE__ */ jsx(InCapsule, { children }) }), target);
|
|
74
82
|
}
|
|
75
83
|
if (usesCapsules) {
|
|
76
|
-
return
|
|
84
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
+
hideOthers,
|
|
86
|
+
createPortal(/* @__PURE__ */ jsx(Box, { role: "group", "aria-label": label, sx: [{ display: "flex", alignItems: "center" }, orderSx, ...extraSx], ...exclusiveProps, children }), target)
|
|
87
|
+
] });
|
|
77
88
|
}
|
|
78
89
|
return createPortal(
|
|
79
90
|
/* @__PURE__ */ jsx(
|
|
@@ -81,7 +92,7 @@ function BarActions({ children, label, order, sx, inlineSx, bare = false }) {
|
|
|
81
92
|
{
|
|
82
93
|
role: "group",
|
|
83
94
|
"aria-label": label,
|
|
84
|
-
sx: [{ display: "flex", alignItems: "center", gap: 0.5, "
|
|
95
|
+
sx: [{ display: "flex", alignItems: "center", gap: 0.5, ...monochromeButtonsSxOf("inherit") }, orderSx, ...extraSx],
|
|
85
96
|
children: /* @__PURE__ */ jsx(InCapsule, { children })
|
|
86
97
|
}
|
|
87
98
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarActions.js","sources":["../../src/appShell/BarActions.jsx"],"sourcesContent":["// Las acciones de UNA pantalla en una cápsula, como las barras de herramientas de Apple: la pantalla las pone en su código\n// donde quiera y el marco decide dónde se ven.\n//\n// - Escritorio con la barra en cápsulas (TOP_BAR.capsules): suben a la barra, cada <BarActions> en su cápsula. Dos grupos\n// que no van juntos (buscar; exportar y agregar) son dos <BarActions>, como Apple separa los grupos.\n// - Teléfono con cápsulas: las mismas cápsulas flotan arriba a la derecha y se quedan al bajar, como los botones de iOS 26\n// (en el teléfono no hay barra de arriba: la de pestañas va abajo). Se ven todas las que caben, solo con ícono; las que\n// ya no caben a lo ancho van a un menú «⋯» con su nombre (useFittingActionCount mide el renglón de verdad). Así una\n// pantalla con muchas acciones arriba no rompe el teléfono.\n// - Escritorio con la franja: entran a la franja como botones (con el color de la franja).\n// - Sin lugar (sin AppLayout, la barra fundida al menú, o el teléfono sin cápsulas): se quedan donde la pantalla las puso,\n// también en una cápsula de solo íconos. PageHeading ya las usa para sus `actions`.\n//\n// Se extiende sin tocar el framework: cualquier contenido adentro (lo normal es BarActionButton, que se acomoda solo), varias\n// por pantalla con su `order`, estilos propios con `sx` (la cápsula de arriba o la flotante) e `inlineSx` (la que se queda en\n// la pantalla), y `bare` cuando lo de adentro ya es una cápsula (el buscador de la tabla). Para una cápsula fuera de todo\n// esto: BarCapsule.\n//\n// OJO: escritorio o teléfono se decide con useMediaQuery (el único lugar del marco): hay que mover los botones a otro lugar\n// del árbol y eso no se puede solo con CSS sin pintarlos dos veces (con sus estados por duplicado). Con Next, el primer\n// pintado los deja en el contenido y se mueven a su lugar al hidratar; con Vite no se nota.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {Children, useState} from 'react';\nimport {createPortal} from 'react-dom';\nimport Box from '@mui/material/Box';\nimport IconButton from '@mui/material/IconButton';\nimport Menu from '@mui/material/Menu';\nimport Tooltip from '@mui/material/Tooltip';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\nimport BarCapsule from '@/appShell/BarCapsule.jsx';\nimport {\n BAR_ACTIONS_PLACEMENT, BarActionsMenuCloseContext, BarActionsPlacementContext, useBarActionsSlot,\n} from '@/appShell/barActionsSlot.js';\nimport {DESKTOP_MEDIA_QUERY} from '@/appShell/shellSizes.js';\nimport useFittingActionCount from '@/appShell/useFittingActionCount.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/** Lo de adentro sabe que quedó en una cápsula (BarActionButton se vuelve solo ícono). */\nconst InCapsule = ({children}) => (\n <BarActionsPlacementContext.Provider value={BAR_ACTIONS_PLACEMENT.capsule}>{children}</BarActionsPlacementContext.Provider>\n);\n\n/** El «⋯» del teléfono con las acciones que no caben, como renglones de menú con su nombre. */\nconst MoreActionsMenu = ({label, children}) => {\n const [anchorElement, setAnchorElement] = useState(null);\n const close = () => setAnchorElement(null);\n return (\n <>\n <Tooltip title={label}>\n <IconButton aria-label={label} aria-haspopup=\"menu\" aria-expanded={Boolean(anchorElement)}\n onClick={(event) => setAnchorElement(event.currentTarget)}>\n <MoreHorizRoundedIcon />\n </IconButton>\n </Tooltip>\n {/* OJO keepMounted: una acción del «⋯» puede abrir algo suyo al picarla (DownloadMenu abre sus formatos y su\n confirmación); sin él, al cerrarse este menú se desmontaba con todo y lo que abrió */}\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={close} keepMounted\n anchorOrigin={{vertical: 'bottom', horizontal: 'right'}} transformOrigin={{vertical: 'top', horizontal: 'right'}}>\n <BarActionsMenuCloseContext.Provider value={close}>\n <BarActionsPlacementContext.Provider value={BAR_ACTIONS_PLACEMENT.menu}>{children}</BarActionsPlacementContext.Provider>\n </BarActionsMenuCloseContext.Provider>\n </Menu>\n </>\n );\n};\n\n/** La cápsula que flota en el teléfono: las que caben como ícono y, si sobran, el «⋯» con las demás. */\nconst PhoneFloatingCapsule = ({phoneSlot, label, sx, children}) => {\n const {t} = useToolkitTranslation('layout');\n const [capsuleElement, setCapsuleElement] = useState(null);\n const actions = Children.toArray(children);\n const visibleCount = useFittingActionCount(phoneSlot, capsuleElement, actions.length);\n const hiddenActions = actions.slice(visibleCount);\n return (\n <BarCapsule ref={setCapsuleElement} aria-label={label} sx={sx}>\n <InCapsule>{actions.slice(0, visibleCount)}</InCapsule>\n {hiddenActions.length > 0 && <MoreActionsMenu label={t('barActions.more')}>{hiddenActions}</MoreActionsMenu>}\n </BarCapsule>\n );\n};\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} props.children los botones de este grupo (normalmente BarActionButton)\n * @param {string} [props.label] el nombre del grupo (lector de pantalla): «Acciones de pedidos»\n * @param {number} [props.order] en qué lugar va entre los grupos de la pantalla (CSS order; por omisión el de aparición)\n * @param {object|Function|Array} [props.sx] estilos propios de la cápsula de arriba o de la flotante del teléfono (o del\n * grupo, en la franja)\n * @param {object|Function|Array} [props.inlineSx] estilos de la cápsula cuando se queda en la pantalla\n * @param {boolean} [props.bare] sin cápsula alrededor: lo de adentro ya es una (un buscador)\n */\nexport default function BarActions({children, label, order, sx, inlineSx, bare = false}) {\n const {slot, phoneSlot, usesCapsules} = useBarActionsSlot();\n const isDesktop = useMediaQuery(DESKTOP_MEDIA_QUERY);\n // en escritorio, la barra; en el teléfono, el renglón flotante (solo existe con cápsulas)\n const target = isDesktop ? slot : phoneSlot;\n const orderSx = order === undefined ? {} : {order};\n const extraSx = Array.isArray(sx) ? sx : [sx];\n const extraInlineSx = Array.isArray(inlineSx) ? inlineSx : [inlineSx];\n\n // sin lugar: donde la pantalla las puso, en su cápsula de íconos\n if (!target) {\n if (bare) {\n return <Box role=\"group\" aria-label={label} sx={[{display: 'flex', alignItems: 'center'}, ...extraInlineSx]}>{children}</Box>;\n }\n return (\n <BarCapsule aria-label={label} sx={[{alignSelf: 'flex-start'}, ...extraInlineSx]}>\n <InCapsule>{children}</InCapsule>\n </BarCapsule>\n );\n }\n // flotando en el teléfono: las que caben y el «⋯»\n if (!isDesktop && !bare) {\n return createPortal(<PhoneFloatingCapsule phoneSlot={phoneSlot} label={label} sx={[orderSx, ...extraSx]}>{children}</PhoneFloatingCapsule>, target);\n }\n // arriba en la barra (o el buscador flotando en el teléfono)\n if (usesCapsules && !bare) {\n return createPortal(<BarCapsule aria-label={label} sx={[orderSx, ...extraSx]}><InCapsule>{children}</InCapsule></BarCapsule>, target);\n }\n if (usesCapsules) {\n return createPortal(<Box role=\"group\" aria-label={label} sx={[{display: 'flex', alignItems: 'center'}, orderSx, ...extraSx]}>{children}</Box>, target);\n }\n // en la franja: botones sueltos (solo ícono) con el color de la franja (sobre el naranja de Stackcraft, el color\n // principal no se vería)\n return createPortal(\n <Box role=\"group\" aria-label={label}\n sx={[{display: 'flex', alignItems: 'center', gap: 0.5, '& .MuiIconButton-root, & .MuiButton-text': {color: 'inherit'}}, orderSx, ...extraSx]}>\n <InCapsule>{children}</InCapsule>\n </Box>,\n target,\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BarActions.js","sources":["../../src/appShell/BarActions.jsx"],"sourcesContent":["// Las acciones de UNA pantalla en una cápsula, como las barras de herramientas de Apple: la pantalla las pone en su código\n// donde quiera y el marco decide dónde se ven.\n//\n// - Escritorio con la barra en cápsulas (TOP_BAR.capsules): suben a la barra, cada <BarActions> en su cápsula. Dos grupos\n// que no van juntos (buscar; exportar y agregar) son dos <BarActions>, como Apple separa los grupos.\n// - Teléfono con cápsulas: las mismas cápsulas flotan arriba a la derecha y se quedan al bajar, como los botones de iOS 26\n// (en el teléfono no hay barra de arriba: la de pestañas va abajo). Se ven todas las que caben, solo con ícono; las que\n// ya no caben a lo ancho van a un menú «⋯» con su nombre (useFittingActionCount mide el renglón de verdad). Así una\n// pantalla con muchas acciones arriba no rompe el teléfono.\n// - Escritorio con la franja: entran a la franja como botones (con el color de la franja).\n// - Sin lugar (sin AppLayout, la barra fundida al menú, o el teléfono sin cápsulas): se quedan donde la pantalla las puso,\n// también en una cápsula de solo íconos. PageHeading ya las usa para sus `actions`.\n//\n// Se extiende sin tocar el framework: cualquier contenido adentro (lo normal es BarActionButton, que se acomoda solo), varias\n// por pantalla con su `order`, estilos propios con `sx` (la cápsula de arriba o la flotante) e `inlineSx` (la que se queda en\n// la pantalla), y `bare` cuando lo de adentro ya es una cápsula (el buscador de la tabla). Para una cápsula fuera de todo\n// esto: BarCapsule.\n//\n// OJO: escritorio o teléfono se decide con useMediaQuery (el único lugar del marco): hay que mover los botones a otro lugar\n// del árbol y eso no se puede solo con CSS sin pintarlos dos veces (con sus estados por duplicado). Con Next, el primer\n// pintado los deja en el contenido y se mueven a su lugar al hidratar; con Vite no se nota.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {Children, useState} from 'react';\nimport {createPortal} from 'react-dom';\nimport Box from '@mui/material/Box';\nimport GlobalStyles from '@mui/material/GlobalStyles';\nimport IconButton from '@mui/material/IconButton';\nimport Menu from '@mui/material/Menu';\nimport Tooltip from '@mui/material/Tooltip';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\nimport BarCapsule, {monochromeButtonsSxOf} from '@/appShell/BarCapsule.jsx';\nimport {\n BAR_ACTIONS_PLACEMENT, BarActionsMenuCloseContext, BarActionsPlacementContext, useBarActionsSlot,\n} from '@/appShell/barActionsSlot.js';\nimport {DESKTOP_MEDIA_QUERY} from '@/appShell/shellSizes.js';\nimport useFittingActionCount from '@/appShell/useFittingActionCount.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n// la marca de un grupo exclusivo (BarActions exclusive) y la regla que, mientras exista, esconde a los demás grupos del\n// renglón flotante del teléfono (data-phone-bar-actions lo pone AppLayout)\nconst EXCLUSIVE_ATTRIBUTE = 'data-bar-exclusive';\nconst HIDE_NOT_EXCLUSIVE_STYLES = {[`[data-phone-bar-actions] > :not([${EXCLUSIVE_ATTRIBUTE}])`]: {display: 'none !important'}};\n\n/** Lo de adentro sabe que quedó en una cápsula (BarActionButton se vuelve solo ícono). */\nconst InCapsule = ({children}) => (\n <BarActionsPlacementContext.Provider value={BAR_ACTIONS_PLACEMENT.capsule}>{children}</BarActionsPlacementContext.Provider>\n);\n\n/** El «⋯» del teléfono con las acciones que no caben, como renglones de menú con su nombre. */\nconst MoreActionsMenu = ({label, children}) => {\n const [anchorElement, setAnchorElement] = useState(null);\n const close = () => setAnchorElement(null);\n return (\n <>\n <Tooltip title={label}>\n <IconButton aria-label={label} aria-haspopup=\"menu\" aria-expanded={Boolean(anchorElement)}\n onClick={(event) => setAnchorElement(event.currentTarget)}>\n <MoreHorizRoundedIcon />\n </IconButton>\n </Tooltip>\n {/* OJO keepMounted: una acción del «⋯» puede abrir algo suyo al picarla (DownloadMenu abre sus formatos y su\n confirmación); sin él, al cerrarse este menú se desmontaba con todo y lo que abrió */}\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={close} keepMounted\n anchorOrigin={{vertical: 'bottom', horizontal: 'right'}} transformOrigin={{vertical: 'top', horizontal: 'right'}}>\n <BarActionsMenuCloseContext.Provider value={close}>\n <BarActionsPlacementContext.Provider value={BAR_ACTIONS_PLACEMENT.menu}>{children}</BarActionsPlacementContext.Provider>\n </BarActionsMenuCloseContext.Provider>\n </Menu>\n </>\n );\n};\n\n/** La cápsula que flota en el teléfono: las que caben como ícono y, si sobran, el «⋯» con las demás. */\nconst PhoneFloatingCapsule = ({phoneSlot, label, sx, texts, children, ...rest}) => {\n const {t} = useToolkitTranslation('layout', texts);\n const [capsuleElement, setCapsuleElement] = useState(null);\n const actions = Children.toArray(children);\n const visibleCount = useFittingActionCount(phoneSlot, capsuleElement, actions.length);\n const hiddenActions = actions.slice(visibleCount);\n return (\n <BarCapsule ref={setCapsuleElement} aria-label={label} sx={sx} {...rest}>\n <InCapsule>{actions.slice(0, visibleCount)}</InCapsule>\n {hiddenActions.length > 0 && <MoreActionsMenu label={t('barActions.more')}>{hiddenActions}</MoreActionsMenu>}\n </BarCapsule>\n );\n};\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} props.children los botones de este grupo (normalmente BarActionButton)\n * @param {string} [props.label] el nombre del grupo (lector de pantalla): «Acciones de pedidos»\n * @param {number} [props.order] en qué lugar va entre los grupos de la pantalla (CSS order; por omisión el de aparición)\n * @param {object|Function|Array} [props.sx] estilos propios de la cápsula de arriba o de la flotante del teléfono (o del\n * grupo, en la franja)\n * @param {object|Function|Array} [props.inlineSx] estilos de la cápsula cuando se queda en la pantalla\n * @param {boolean} [props.bare] sin cápsula alrededor: lo de adentro ya es una (un buscador)\n * @param {boolean} [props.exclusive] en el teléfono, mientras se ve, es lo único del renglón flotante (con los demás grupos\n * exclusivos): los otros se esconden, como el modo de edición del iPhone deja solo lo de lo elegido. Lo usa SelectionActions\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nexport default function BarActions({children, label, order, sx, inlineSx, bare = false, exclusive = false, texts}) {\n const {slot, phoneSlot, usesCapsules} = useBarActionsSlot();\n const isDesktop = useMediaQuery(DESKTOP_MEDIA_QUERY);\n // en escritorio, la barra; en el teléfono, el renglón flotante (solo existe con cápsulas)\n const target = isDesktop ? slot : phoneSlot;\n const orderSx = order === undefined ? {} : {order};\n const extraSx = Array.isArray(sx) ? sx : [sx];\n const extraInlineSx = Array.isArray(inlineSx) ? inlineSx : [inlineSx];\n\n // sin lugar: donde la pantalla las puso, en su cápsula de íconos\n if (!target) {\n if (bare) {\n return <Box role=\"group\" aria-label={label} sx={[{display: 'flex', alignItems: 'center'}, ...extraInlineSx]}>{children}</Box>;\n }\n return (\n <BarCapsule aria-label={label} sx={[{alignSelf: 'flex-start'}, ...extraInlineSx]}>\n <InCapsule>{children}</InCapsule>\n </BarCapsule>\n );\n }\n // exclusivo en el teléfono: se marca y esconde a los demás grupos del renglón mientras se ve. OJO: sin esto, con algo\n // elegido no cabían el conteo, sus acciones, la lupa y las de la pantalla, y el renglón se salía por los dos lados\n const exclusiveProps = !isDesktop && exclusive ? {[EXCLUSIVE_ATTRIBUTE]: ''} : {};\n const hideOthers = !isDesktop && exclusive && <GlobalStyles styles={HIDE_NOT_EXCLUSIVE_STYLES} />;\n // flotando en el teléfono: las que caben y el «⋯»\n if (!isDesktop && !bare) {\n return (\n <>\n {hideOthers}\n {createPortal(<PhoneFloatingCapsule phoneSlot={phoneSlot} label={label} sx={[orderSx, ...extraSx]} texts={texts} {...exclusiveProps}>{children}</PhoneFloatingCapsule>, target)}\n </>\n );\n }\n // arriba en la barra (o el buscador flotando en el teléfono)\n if (usesCapsules && !bare) {\n return createPortal(<BarCapsule aria-label={label} sx={[orderSx, ...extraSx]}><InCapsule>{children}</InCapsule></BarCapsule>, target);\n }\n if (usesCapsules) {\n return (\n <>\n {hideOthers}\n {createPortal(<Box role=\"group\" aria-label={label} sx={[{display: 'flex', alignItems: 'center'}, orderSx, ...extraSx]} {...exclusiveProps}>{children}</Box>, target)}\n </>\n );\n }\n // en la franja: botones sueltos (solo ícono) con el color de la franja (sobre el naranja de Stackcraft, el color\n // principal no se vería)\n return createPortal(\n <Box role=\"group\" aria-label={label}\n sx={[{display: 'flex', alignItems: 'center', gap: 0.5, ...monochromeButtonsSxOf('inherit')}, orderSx, ...extraSx]}>\n <InCapsule>{children}</InCapsule>\n </Box>,\n target,\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,22 @@ import Box from "@mui/material/Box";
|
|
|
4
4
|
import { paletteOf } from "../theme/components/paletteOf.js";
|
|
5
5
|
import { isGlassTheme, glassStylesOf } from "../theme/glassSurface.js";
|
|
6
6
|
const BAR_CAPSULE_HEIGHT_PIXELS = 48;
|
|
7
|
+
function monochromeButtonsSxOf(color) {
|
|
8
|
+
return {
|
|
9
|
+
"& .MuiIconButton-root:not(.Mui-disabled), & .MuiButton-root:not(.Mui-disabled)": { color },
|
|
10
|
+
// un botón con relleno o con borde se queda como uno de texto: sin fondo, sin borde y sin sombra, también al pasar el
|
|
11
|
+
// cursor (ahí lleva el velo de cualquier botón de texto). OJO: la barra es un Paper y el tema tiñe los botones con
|
|
12
|
+
// borde «dentro de una tarjeta» con body[data-glass-lens] .MuiPaper-root … (cuatro clases y un elemento): por eso
|
|
13
|
+
// .MuiButton-root va repetido, para pesar cinco clases (con menos, «Restablecer» seguía teñido; medido en Chrome)
|
|
14
|
+
"& .MuiButton-root.MuiButton-root.MuiButton-root.MuiButton-contained, & .MuiButton-root.MuiButton-root.MuiButton-root.MuiButton-outlined": {
|
|
15
|
+
backgroundColor: "transparent",
|
|
16
|
+
backgroundImage: "none",
|
|
17
|
+
boxShadow: "none",
|
|
18
|
+
border: 0,
|
|
19
|
+
"&:hover": { backgroundColor: "action.hover", boxShadow: "none" }
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
7
23
|
function BarCapsule({ children, sx, role = "group", ...rest }) {
|
|
8
24
|
return /* @__PURE__ */ jsx(
|
|
9
25
|
Box,
|
|
@@ -12,9 +28,7 @@ function BarCapsule({ children, sx, role = "group", ...rest }) {
|
|
|
12
28
|
...rest,
|
|
13
29
|
sx: [(theme) => ({
|
|
14
30
|
...isGlassTheme(theme) ? glassStylesOf(theme, { isControl: true }) : { backgroundColor: paletteOf(theme).background.paper },
|
|
15
|
-
...isGlassTheme(theme) &&
|
|
16
|
-
"& .MuiIconButton-root:not(.Mui-disabled), & .MuiButton-root:not(.Mui-disabled)": { color: paletteOf(theme).text.primary }
|
|
17
|
-
},
|
|
31
|
+
...isGlassTheme(theme) && monochromeButtonsSxOf(paletteOf(theme).text.primary),
|
|
18
32
|
display: "flex",
|
|
19
33
|
alignItems: "center",
|
|
20
34
|
gap: 0.5,
|
|
@@ -30,6 +44,7 @@ function BarCapsule({ children, sx, role = "group", ...rest }) {
|
|
|
30
44
|
}
|
|
31
45
|
export {
|
|
32
46
|
BAR_CAPSULE_HEIGHT_PIXELS,
|
|
33
|
-
BarCapsule as default
|
|
47
|
+
BarCapsule as default,
|
|
48
|
+
monochromeButtonsSxOf
|
|
34
49
|
};
|
|
35
50
|
//# sourceMappingURL=BarCapsule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarCapsule.js","sources":["../../src/appShell/BarCapsule.jsx"],"sourcesContent":["// Una cápsula de la barra de arriba (como las de las ventanas de macOS Tahoe): un grupo de botones juntos, con vidrio si el\n// tema lo trae y, con el minimalista, un bloque del tono de las tarjetas sin borde ni sombra. La usan el marco (la ☰ con el\n// nombre, la cuenta), las acciones de cada pantalla (BarActions) y el proyecto para algo suyo que deba verse igual (unos\n// botones encima de un mapa, una barra de herramientas propia).\n//\n// Con vidrio, los botones de adentro van SIEMPRE del color del texto, aunque quien los usa les ponga color (color=\"primary\"\
|
|
1
|
+
{"version":3,"file":"BarCapsule.js","sources":["../../src/appShell/BarCapsule.jsx"],"sourcesContent":["// Una cápsula de la barra de arriba (como las de las ventanas de macOS Tahoe): un grupo de botones juntos, con vidrio si el\n// tema lo trae y, con el minimalista, un bloque del tono de las tarjetas sin borde ni sombra. La usan el marco (la ☰ con el\n// nombre, la cuenta), las acciones de cada pantalla (BarActions) y el proyecto para algo suyo que deba verse igual (unos\n// botones encima de un mapa, una barra de herramientas propia).\n//\n// Con vidrio, los botones de adentro van SIEMPRE del color del texto y sin relleno, borde ni sombra, aunque quien los usa\n// les ponga color o variante (color=\"primary\", variant=\"contained\" u \"outlined\", un sx propio): en las barras de Apple los\n// símbolos son de un solo color y el color queda para el contenido (HIG Toolbars y Color). Lo pidió el autor. OJO: al\n// principio solo se quitó el color del texto y un «Redactar» con relleno seguía naranja en la barra (lo cachó el autor). Se\n// logra con selectores de más clases que las del botón; los deshabilitados se quedan con su gris. Con el minimalista\n// conservan su color.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Box from '@mui/material/Box';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\n\n// lo alto de una cápsula (px): cabe en el alto de siempre de la barra con aire arriba y abajo\nexport const BAR_CAPSULE_HEIGHT_PIXELS = 48;\n\n/**\n * Los botones de adentro de un solo color y sin relleno, borde ni sombra (la regla de la barra con vidrio). La usan también\n * la franja (BarActions) y lo que se ponga junto a la barra.\n *\n * @param {string} color el del texto de la barra ('inherit' en la franja)\n * @returns {object} estilos para el sx del contenedor\n */\nexport function monochromeButtonsSxOf(color) {\n return {\n '& .MuiIconButton-root:not(.Mui-disabled), & .MuiButton-root:not(.Mui-disabled)': {color},\n // un botón con relleno o con borde se queda como uno de texto: sin fondo, sin borde y sin sombra, también al pasar el\n // cursor (ahí lleva el velo de cualquier botón de texto). OJO: la barra es un Paper y el tema tiñe los botones con\n // borde «dentro de una tarjeta» con body[data-glass-lens] .MuiPaper-root … (cuatro clases y un elemento): por eso\n // .MuiButton-root va repetido, para pesar cinco clases (con menos, «Restablecer» seguía teñido; medido en Chrome)\n '& .MuiButton-root.MuiButton-root.MuiButton-root.MuiButton-contained, & .MuiButton-root.MuiButton-root.MuiButton-root.MuiButton-outlined': {\n backgroundColor: 'transparent', backgroundImage: 'none', boxShadow: 'none', border: 0,\n '&:hover': {backgroundColor: 'action.hover', boxShadow: 'none'},\n },\n };\n}\n\n/**\n * Lo demás va al div de la cápsula: aria-label (el nombre del grupo para quien usa lector de pantalla), id, onClick…\n * role va por omisión en 'group' (lo que se agrupa se anuncia junto).\n *\n * @param {Omit<import('react').HTMLAttributes<HTMLDivElement>, 'children'> & {children: import('react').ReactNode, sx?: object|Function|Array<any>}} props\n * children: los botones del grupo; sx: estilos propios, encima de los de la cápsula\n */\nexport default function BarCapsule({children, sx, role = 'group', ...rest}) {\n return (\n <Box role={role} {...rest}\n sx={[(theme) => ({\n ...(isGlassTheme(theme) ? glassStylesOf(theme, {isControl: true}) : {backgroundColor: paletteOf(theme).background.paper}),\n ...(isGlassTheme(theme) && monochromeButtonsSxOf(paletteOf(theme).text.primary)),\n display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0, minWidth: 0,\n height: BAR_CAPSULE_HEIGHT_PIXELS, px: 0.75, borderRadius: '999px',\n }), ...(Array.isArray(sx) ? sx : [sx])]}>\n {children}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;AAmBO,MAAM,4BAA4B;AASlC,SAAS,sBAAsB,OAAO;AACzC,SAAO;AAAA,IACH,kFAAkF,EAAC,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKnF,2IAA2I;AAAA,MACvI,iBAAiB;AAAA,MAAe,iBAAiB;AAAA,MAAQ,WAAW;AAAA,MAAQ,QAAQ;AAAA,MACpF,WAAW,EAAC,iBAAiB,gBAAgB,WAAW,OAAA;AAAA,IAAM;AAAA,EAClE;AAER;AASA,SAAwB,WAAW,EAAC,UAAU,IAAI,OAAO,SAAS,GAAG,QAAO;AACxE,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAI;AAAA,MAAa,GAAG;AAAA,MAChB,IAAI,CAAC,CAAC,WAAW;AAAA,QACb,GAAI,aAAa,KAAK,IAAI,cAAc,OAAO,EAAC,WAAW,KAAA,CAAK,IAAI,EAAC,iBAAiB,UAAU,KAAK,EAAE,WAAW,MAAA;AAAA,QAClH,GAAI,aAAa,KAAK,KAAK,sBAAsB,UAAU,KAAK,EAAE,KAAK,OAAO;AAAA,QAC9E,SAAS;AAAA,QAAQ,YAAY;AAAA,QAAU,KAAK;AAAA,QAAK,YAAY;AAAA,QAAG,UAAU;AAAA,QAC1E,QAAQ;AAAA,QAA2B,IAAI;AAAA,QAAM,cAAc;AAAA,MAAA,IAC3D,GAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,CAAC,EAAE,CAAE;AAAA,MACtC;AAAA,IAAA;AAAA,EAAA;AAGb;"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import InputAdornment from "@mui/material/InputAdornment";
|
|
5
|
+
import TextField from "@mui/material/TextField";
|
|
6
|
+
import SearchIcon from "@mui/icons-material/Search";
|
|
7
|
+
import BarActions from "./BarActions.js";
|
|
8
|
+
import { BAR_CAPSULE_HEIGHT_PIXELS } from "./BarCapsule.js";
|
|
9
|
+
import { PHONE_MEDIA_QUERY } from "./shellSizes.js";
|
|
10
|
+
const SEARCH_BOX_MIN_WIDTH_PIXELS = 400;
|
|
11
|
+
const SEARCH_IN_BAR_WIDTH_PIXELS = 380;
|
|
12
|
+
function BarSearch({ value, onChange, placeholder, inBar = true, collapsible = true, order = 0, onFocus, onBlur, inputRef }) {
|
|
13
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
14
|
+
const isCompact = inBar && collapsible && !isFocused && !value;
|
|
15
|
+
const searchBox = /* @__PURE__ */ jsx(
|
|
16
|
+
TextField,
|
|
17
|
+
{
|
|
18
|
+
type: "search",
|
|
19
|
+
size: "small",
|
|
20
|
+
inputRef,
|
|
21
|
+
value,
|
|
22
|
+
onChange: (event) => onChange(event.target.value),
|
|
23
|
+
onFocus: () => {
|
|
24
|
+
setIsFocused(true);
|
|
25
|
+
onFocus?.();
|
|
26
|
+
},
|
|
27
|
+
onBlur: () => {
|
|
28
|
+
setIsFocused(false);
|
|
29
|
+
onBlur?.();
|
|
30
|
+
},
|
|
31
|
+
onKeyDown: (event) => {
|
|
32
|
+
if (event.key === "Escape") {
|
|
33
|
+
onChange("");
|
|
34
|
+
event.currentTarget.querySelector("input")?.blur();
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
placeholder,
|
|
38
|
+
sx: {
|
|
39
|
+
minWidth: { xs: 0, md: inBar ? 0 : SEARCH_BOX_MIN_WIDTH_PIXELS },
|
|
40
|
+
// la × del navegador para type="search" no va: la caja se borra con Escape
|
|
41
|
+
'& input[type="search"]::-webkit-search-cancel-button': { WebkitAppearance: "none", display: "none" }
|
|
42
|
+
},
|
|
43
|
+
slotProps: {
|
|
44
|
+
input: { startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(SearchIcon, { fontSize: "small" }) }) },
|
|
45
|
+
htmlInput: { "aria-label": placeholder }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
if (!inBar) {
|
|
50
|
+
return searchBox;
|
|
51
|
+
}
|
|
52
|
+
return /* @__PURE__ */ jsx(
|
|
53
|
+
BarActions,
|
|
54
|
+
{
|
|
55
|
+
label: placeholder,
|
|
56
|
+
bare: true,
|
|
57
|
+
order,
|
|
58
|
+
sx: (theme) => ({
|
|
59
|
+
// en el teléfono flota arriba junto a las demás cápsulas. Su lugar SIEMPRE ocupa lo libre del renglón y
|
|
60
|
+
// la caja va pegada a la derecha: así se abre de derecha a izquierda y regresa igual (el ancho pasa de 48 px
|
|
61
|
+
// a 100 % con transición). OJO: antes el lugar brincaba de golpe (flex-grow no se anima) y la caja crecía
|
|
62
|
+
// de izquierda a derecha. El hueco de la izquierda deja pasar el dedo a lo de abajo; solo la caja lo recibe
|
|
63
|
+
[PHONE_MEDIA_QUERY]: {
|
|
64
|
+
flexGrow: 1,
|
|
65
|
+
minWidth: 0,
|
|
66
|
+
justifyContent: "flex-end",
|
|
67
|
+
pointerEvents: "none",
|
|
68
|
+
"& .MuiTextField-root": { pointerEvents: "auto" }
|
|
69
|
+
},
|
|
70
|
+
"& .MuiTextField-root": {
|
|
71
|
+
width: isCompact ? BAR_CAPSULE_HEIGHT_PIXELS : SEARCH_IN_BAR_WIDTH_PIXELS,
|
|
72
|
+
minWidth: 0,
|
|
73
|
+
transition: theme.transitions.create("width", { duration: theme.transitions.duration.short }),
|
|
74
|
+
"@media (prefers-reduced-motion: reduce)": { transition: "none" },
|
|
75
|
+
[PHONE_MEDIA_QUERY]: { width: isCompact ? BAR_CAPSULE_HEIGHT_PIXELS : "100%" }
|
|
76
|
+
},
|
|
77
|
+
"& .MuiFormControl-root .MuiInputBase-root.MuiOutlinedInput-root": {
|
|
78
|
+
height: BAR_CAPSULE_HEIGHT_PIXELS,
|
|
79
|
+
borderRadius: "999px",
|
|
80
|
+
cursor: isCompact ? "pointer" : "text"
|
|
81
|
+
},
|
|
82
|
+
"& .MuiInputAdornment-root": { pointerEvents: "none" },
|
|
83
|
+
"& input::placeholder": { opacity: isCompact ? 0 : void 0 }
|
|
84
|
+
}),
|
|
85
|
+
children: searchBox
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
BarSearch as default
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=BarSearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BarSearch.js","sources":["../../src/appShell/BarSearch.jsx"],"sourcesContent":["// Una caja de búsqueda que sube a la barra de la app, como el buscador de las ventanas de macOS: sin usar se encoge a la\n// lupa, al picarla (o llegar con Tab) se estira, y con texto se queda abierta. En el teléfono con cápsulas flota arriba con\n// las demás; sin barra en cápsulas se queda donde la pantalla la puso. Escape borra lo escrito y la suelta.\n//\n// <BarSearch value={query} onChange={setQuery} placeholder=\"Buscar por título o etiqueta\" />\n//\n// La usan la tabla de servidor (searchInBar) y cualquier pantalla que busque en lo suyo (Tareas). Con inBar={false} es la\n// misma caja dentro de la pantalla.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\nimport SearchIcon from '@mui/icons-material/Search';\n\nimport BarActions from '@/appShell/BarActions.jsx';\nimport {BAR_CAPSULE_HEIGHT_PIXELS} from '@/appShell/BarCapsule.jsx';\nimport {PHONE_MEDIA_QUERY} from '@/appShell/shellSizes.js';\n\n// ancho de la caja dentro de la pantalla en escritorio: cabe un texto de ayuda como «Buscar por pedido, cliente o producto…».\n// OJO: con 280 se cortaba («…cliente o pro»); en el teléfono la caja toma todo el ancho\nconst SEARCH_BOX_MIN_WIDTH_PIXELS = 400;\n// en la barra un poco más angosta: comparte el renglón con el título y las cápsulas\nconst SEARCH_IN_BAR_WIDTH_PIXELS = 380;\n\n/**\n * @param {object} props\n * @param {string} props.value lo escrito\n * @param {(value: string) => void} props.onChange recibe el texto, no el evento\n * @param {string} props.placeholder ya traducido: qué se busca («Buscar por título o etiqueta»); también es su nombre para el\n * lector de pantalla\n * @param {boolean} [props.inBar] en la barra de la app (por omisión) o dentro de la pantalla\n * @param {boolean} [props.collapsible] en la barra, sin usar se encoge a la lupa; por omisión sí (false: siempre abierta)\n * @param {number} [props.order] en qué lugar va entre las cápsulas de la barra (por omisión la primera)\n * @param {() => void} [props.onFocus]\n * @param {() => void} [props.onBlur]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef] la caja, para enfocarla desde afuera (un atajo «/»)\n */\nexport default function BarSearch({value, onChange, placeholder, inBar = true, collapsible = true, order = 0, onFocus, onBlur, inputRef}) {\n const [isFocused, setIsFocused] = useState(false);\n // en la barra, chica cuando no se usa: ni tiene el foco ni tiene texto\n const isCompact = inBar && collapsible && !isFocused && !value;\n const searchBox = (\n <TextField\n type=\"search\"\n size=\"small\"\n inputRef={inputRef}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n onFocus={() => {\n setIsFocused(true);\n onFocus?.();\n }}\n onBlur={() => {\n setIsFocused(false);\n onBlur?.();\n }}\n // Escape borra lo escrito y suelta la caja (en la barra, regresa a la lupa)\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n onChange('');\n event.currentTarget.querySelector('input')?.blur();\n }\n }}\n placeholder={placeholder}\n sx={{\n minWidth: {xs: 0, md: inBar ? 0 : SEARCH_BOX_MIN_WIDTH_PIXELS},\n // la × del navegador para type=\"search\" no va: la caja se borra con Escape\n '& input[type=\"search\"]::-webkit-search-cancel-button': {WebkitAppearance: 'none', display: 'none'},\n }}\n slotProps={{\n input: {startAdornment: <InputAdornment position=\"start\"><SearchIcon fontSize=\"small\" /></InputAdornment>},\n htmlInput: {'aria-label': placeholder},\n }}\n />\n );\n\n if (!inBar) {\n return searchBox;\n }\n\n // en la barra: la caja ya es su propia cápsula (bare), del alto y la forma de las demás y más angosta.\n // OJO: la barra es un Paper y el tema le pone al campo el radio de «campo dentro de tarjeta», con el lente\n // (body[data-glass-lens] .MuiPaper-root …) aún más específica: por eso las tres clases (con menos salía con esquinas de\n // 12 px y no en cápsula; medido en Chrome). La lupa no atrapa el clic: lo deja pasar a la caja, que es la que se enfoca\n return (\n <BarActions label={placeholder} bare order={order}\n sx={(theme) => ({\n // en el teléfono flota arriba junto a las demás cápsulas. Su lugar SIEMPRE ocupa lo libre del renglón y\n // la caja va pegada a la derecha: así se abre de derecha a izquierda y regresa igual (el ancho pasa de 48 px\n // a 100 % con transición). OJO: antes el lugar brincaba de golpe (flex-grow no se anima) y la caja crecía\n // de izquierda a derecha. El hueco de la izquierda deja pasar el dedo a lo de abajo; solo la caja lo recibe\n [PHONE_MEDIA_QUERY]: {\n flexGrow: 1, minWidth: 0, justifyContent: 'flex-end', pointerEvents: 'none',\n '& .MuiTextField-root': {pointerEvents: 'auto'},\n },\n '& .MuiTextField-root': {\n width: isCompact ? BAR_CAPSULE_HEIGHT_PIXELS : SEARCH_IN_BAR_WIDTH_PIXELS,\n minWidth: 0,\n transition: theme.transitions.create('width', {duration: theme.transitions.duration.short}),\n '@media (prefers-reduced-motion: reduce)': {transition: 'none'},\n [PHONE_MEDIA_QUERY]: {width: isCompact ? BAR_CAPSULE_HEIGHT_PIXELS : '100%'},\n },\n '& .MuiFormControl-root .MuiInputBase-root.MuiOutlinedInput-root': {\n height: BAR_CAPSULE_HEIGHT_PIXELS, borderRadius: '999px', cursor: isCompact ? 'pointer' : 'text',\n },\n '& .MuiInputAdornment-root': {pointerEvents: 'none'},\n '& input::placeholder': {opacity: isCompact ? 0 : undefined},\n })}>\n {searchBox}\n </BarActions>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;AAqBA,MAAM,8BAA8B;AAEpC,MAAM,6BAA6B;AAenC,SAAwB,UAAU,EAAC,OAAO,UAAU,aAAa,QAAQ,MAAM,cAAc,MAAM,QAAQ,GAAG,SAAS,QAAQ,YAAW;AACtI,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,QAAM,YAAY,SAAS,eAAe,CAAC,aAAa,CAAC;AACzD,QAAM,YACF;AAAA,IAAC;AAAA,IAAA;AAAA,MACG,MAAK;AAAA,MACL,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,SAAS,MAAM;AACX,qBAAa,IAAI;AACjB,kBAAA;AAAA,MACJ;AAAA,MACA,QAAQ,MAAM;AACV,qBAAa,KAAK;AAClB,iBAAA;AAAA,MACJ;AAAA,MAEA,WAAW,CAAC,UAAU;AAClB,YAAI,MAAM,QAAQ,UAAU;AACxB,mBAAS,EAAE;AACX,gBAAM,cAAc,cAAc,OAAO,GAAG,KAAA;AAAA,QAChD;AAAA,MACJ;AAAA,MACA;AAAA,MACA,IAAI;AAAA,QACA,UAAU,EAAC,IAAI,GAAG,IAAI,QAAQ,IAAI,4BAAA;AAAA;AAAA,QAElC,wDAAwD,EAAC,kBAAkB,QAAQ,SAAS,OAAA;AAAA,MAAM;AAAA,MAEtG,WAAW;AAAA,QACP,OAAO,EAAC,gBAAgB,oBAAC,gBAAA,EAAe,UAAS,SAAQ,UAAA,oBAAC,YAAA,EAAW,UAAS,QAAA,CAAQ,EAAA,CAAE,EAAA;AAAA,QACxF,WAAW,EAAC,cAAc,YAAA;AAAA,MAAW;AAAA,IACzC;AAAA,EAAA;AAIR,MAAI,CAAC,OAAO;AACR,WAAO;AAAA,EACX;AAMA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAW,OAAO;AAAA,MAAa,MAAI;AAAA,MAAC;AAAA,MACzB,IAAI,CAAC,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA,QAKZ,CAAC,iBAAiB,GAAG;AAAA,UACjB,UAAU;AAAA,UAAG,UAAU;AAAA,UAAG,gBAAgB;AAAA,UAAY,eAAe;AAAA,UACrE,wBAAwB,EAAC,eAAe,OAAA;AAAA,QAAM;AAAA,QAElD,wBAAwB;AAAA,UACpB,OAAO,YAAY,4BAA4B;AAAA,UAC/C,UAAU;AAAA,UACV,YAAY,MAAM,YAAY,OAAO,SAAS,EAAC,UAAU,MAAM,YAAY,SAAS,MAAA,CAAM;AAAA,UAC1F,2CAA2C,EAAC,YAAY,OAAA;AAAA,UACxD,CAAC,iBAAiB,GAAG,EAAC,OAAO,YAAY,4BAA4B,OAAA;AAAA,QAAM;AAAA,QAE/E,mEAAmE;AAAA,UAC/D,QAAQ;AAAA,UAA2B,cAAc;AAAA,UAAS,QAAQ,YAAY,YAAY;AAAA,QAAA;AAAA,QAE9F,6BAA6B,EAAC,eAAe,OAAA;AAAA,QAC7C,wBAAwB,EAAC,SAAS,YAAY,IAAI,OAAA;AAAA,MAAS;AAAA,MAEtE,UAAA;AAAA,IAAA;AAAA,EAAA;AAGb;"}
|
|
@@ -144,7 +144,7 @@ function useFittingTabCount(capsule, minTabPixels) {
|
|
|
144
144
|
}, [capsule, minTabPixels]);
|
|
145
145
|
return tabCount;
|
|
146
146
|
}
|
|
147
|
-
function MobileTabBar({ menuItems, activeItem, menuBadges, account, moreFooter, minimizeOnScroll = true }) {
|
|
147
|
+
function MobileTabBar({ menuItems, activeItem, menuBadges, account, moreFooter, minimizeOnScroll = true, onPinnedTabsChange }) {
|
|
148
148
|
const { t } = useToolkitTranslation("layout");
|
|
149
149
|
const { Link: RouterLink } = useToolkitNavigation();
|
|
150
150
|
const { pinnedKeys, setPinnedKeys } = usePinnedMobileTabs();
|
|
@@ -314,10 +314,12 @@ function MobileTabBar({ menuItems, activeItem, menuBadges, account, moreFooter,
|
|
|
314
314
|
pinnedKeys: tabs.map(menuItemKeyOf),
|
|
315
315
|
onDone: (keys) => {
|
|
316
316
|
setPinnedKeys(keys);
|
|
317
|
+
onPinnedTabsChange?.(keys);
|
|
317
318
|
leaveEditor();
|
|
318
319
|
},
|
|
319
320
|
onReset: () => {
|
|
320
321
|
setPinnedKeys(null);
|
|
322
|
+
onPinnedTabsChange?.(null);
|
|
321
323
|
leaveEditor();
|
|
322
324
|
}
|
|
323
325
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileTabBar.js","sources":["../../src/appShell/MobileTabBar.jsx"],"sourcesContent":["// La barra de pestañas del teléfono con vidrio, como la de iOS 26: una cápsula de vidrio flotando abajo con las secciones\n// principales (mobileTabsOf: las que caben a lo ancho, y «Más» con el resto), y aparte, en su círculo de vidrio, la cuenta\n// (con todos los ajustes adentro). La reemplaza a la barra de abajo con ☰ y al cajón del menú (lo pidió el autor: «estilo\n// Apple pero con sus submenús de acciones»). También va con el minimalista cuando la barra va en cápsulas (usesMobileTabBar), sólida y\n// del gris de lo que flota (minimalFloatingStylesOf).\n//\n// - Una pestaña con submenú no navega: abre el menú de acciones de Apple arriba de ella (SubmenuActionsMenu).\n// - «Más» abre una hoja desde abajo con el resto del menú, por secciones (SideMenu, el mismo del escritorio). Arriba trae\n// «Editar la barra»: cada quien elige qué pantallas van abajo (TabBarEditor; se recuerda con usePinnedMobileTabs),\n// como pide la guía de Apple. Sin elegir, van las que marca el proyecto (mobileTab en menu.js).\n// - Cada pestaña lleva su badge como los de Apple (HIG «Tab bars»): ROJO con el número en blanco y solo para conteos (lo\n// pendiente, del servidor); un badge de texto («Nuevo») no va en la pestaña, se queda en el menú: Apple los reserva para\n// lo que pide atención.\n// - Cuántas pestañas van se mide (useFittingTabCount): las que caben con al menos MIN_TAB_PIXELS cada una; las demás van\n// a «Más». En una ventana angosta salen menos y en una ancha más, y los nombres no se cortan («Compone…»).\n// - Acostado (pantalla bajita), la barra va compacta: ícono y nombre lado a lado, para dejarle el alto al contenido.\n// - Al leer hacia abajo se encoge (como iOS 26): queda una cápsula chica con la pestaña donde está la persona, y la cuenta\n// más chica; al subir un poco, o al tocar la cápsula, regresa completa (cuándo: tabBarScroll.js). Se mueve solo con\n// opacidad y escala, lo que el navegador anima sin volver a acomodar la página: en un teléfono barato no se traba.\n// Solo se ve en el teléfono (PHONE_MEDIA_QUERY); en escritorio no ocupa nada.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useLayoutEffect, useState} from 'react';\nimport Badge from '@mui/material/Badge';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Drawer from '@mui/material/Drawer';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\nimport {menuItemLabelOf} from '@/appShell/menuItemText.js';\nimport {\n GLASS_PANEL_GAP_PIXELS, MOBILE_TAB_BAR_HEIGHT_PIXELS, PHONE_LANDSCAPE_MEDIA_QUERY, PHONE_MEDIA_QUERY,\n} from '@/appShell/shellSizes.js';\nimport SideMenu from '@/appShell/SideMenu.jsx';\nimport SubmenuActionsMenu, {SUBMENU_PLACEMENT} from '@/appShell/SubmenuActionsMenu.jsx';\nimport TabBarEditor from '@/appShell/TabBarEditor.jsx';\nimport {usePinnedMobileTabs} from '@/appShell/usePinnedMobileTabs.js';\nimport {useTabBarMinimized} from '@/appShell/useTabBarMinimized.js';\nimport {menuBadgeOf, menuItemKeyOf, mobileTabsOf} from '@/navigation/appMenu.js';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {accentTextStylesOf, glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {mergeStyles} from '@/theme/mergeComponents.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n// el nombre de la pestaña parado y acostado (px), lo ancho máximo de la cápsula y lo alto de la hoja de «Más»\nconst TAB_LABEL_PIXELS = Object.freeze({portrait: 10, landscape: 12});\nconst TAB_ICON_SIZE = '1.5rem';\nconst TAB_BAR_MAX_WIDTH_PIXELS = 560;\n// lo menos que mide una pestaña (px) para que su nombre se lea entero: parada, un nombre de unas 11 letras («Formularios»\n// pide 61 px, medido en Chrome); acostada, ícono y nombre lado a lado. Con menos, la pestaña se va a «Más»\nconst MIN_TAB_PIXELS = Object.freeze({portrait: 60, landscape: 104});\n// antes de medir (o si la barra no se ve): las de siempre, 4 y «Más»\nconst DEFAULT_TAB_COUNT = 5;\nconst MORE_SHEET_MAX_WIDTH_PIXELS = 640;\nconst MORE_SHEET_MAX_HEIGHT = '75vh';\nconst BADGE_MAX = 99;\n// la barra flota sobre el contenido: vidrio más opaco que el de los controles, una línea fina en la orilla y una sombra\n// debajo. Con el vidrio de siempre, en claro se perdía sobre las tarjetas blancas y no parecía flotar (lo reportaron)\nconst FLOATING_GLASS_OPACITY = 0.8;\nconst FLOATING_EDGE = Object.freeze({\n light: {outline: 'rgba(15, 23, 42, 0.12)', shadow: '0 10px 30px rgba(15, 23, 42, 0.20), 0 2px 6px rgba(15, 23, 42, 0.10)'},\n dark: {outline: 'rgba(255, 255, 255, 0.16)', shadow: '0 10px 30px rgba(0, 0, 0, 0.65), 0 2px 6px rgba(0, 0, 0, 0.4)'},\n});\n// la pestaña elegida: un tinte del acento (en claro una gota blanca sobre la cápsula blanca no se distinguía)\nconst ACTIVE_TINT_PERCENT = Object.freeze({light: 14, dark: 24});\n// encogida: lo alto de la cápsula chica (px) y a qué tamaño se va lo que desaparece\nconst MINIMIZED_PIXELS = Object.freeze({portrait: 48, landscape: 40});\nconst HIDDEN_SCALE = 0.6;\n\n/**\n * Lo que aparece o desaparece al encoger la barra: solo opacidad y escala. visibility espera a que termine la animación\n * para esconderlo, y así lo que ya no se ve tampoco se puede tocar.\n *\n * @param {object} theme\n * @param {boolean} isShown\n * @param {string} transformOrigin hacia dónde se encoge ('left bottom')\n * @returns {object}\n */\nfunction appearStylesOf(theme, isShown, transformOrigin) {\n const duration = theme.transitions.duration.short;\n return {\n transformOrigin,\n opacity: isShown ? 1 : 0,\n transform: isShown ? 'none' : `scale(${HIDDEN_SCALE})`,\n visibility: isShown ? 'visible' : 'hidden',\n transition: `${theme.transitions.create(['opacity', 'transform'], {duration})}, visibility 0s linear ${isShown ? 0 : duration}ms`,\n };\n}\n\n/**\n * La superficie de la barra y del círculo de la cuenta: vidrio más opaco, su línea fina y su sombra (la sombra va en una\n * capa detrás, ::before, para no pisar el filo de luz del vidrio, que también es una sombra). Con el minimalista, sólida.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction floatingSurfaceOf(theme) {\n // con el minimalista (barra en cápsulas) la barra es sólida: su gris de lo que flota, sin línea ni sombra\n if (!isGlassTheme(theme)) {\n return {...minimalFloatingStylesOf(theme), position: 'relative', borderRadius: '999px', height: '100%'};\n }\n return {\n ...glassStylesOf(theme, {opacity: FLOATING_GLASS_OPACITY}),\n position: 'relative',\n borderRadius: '999px',\n height: '100%',\n outline: `1px solid ${FLOATING_EDGE.light.outline}`,\n '&::before': {\n content: '\"\"', position: 'absolute', inset: 0, borderRadius: 'inherit', zIndex: -1, pointerEvents: 'none',\n boxShadow: FLOATING_EDGE.light.shadow,\n },\n ...theme.applyStyles('dark', {\n outline: `1px solid ${FLOATING_EDGE.dark.outline}`,\n '&::before': {boxShadow: FLOATING_EDGE.dark.shadow},\n }),\n };\n}\n\n/**\n * La pestaña elegida: el tinte del acento y su letra legible.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction activeTabOf(theme) {\n const primary = (theme.vars ?? theme).palette.primary.main;\n return mergeStyles({\n backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.light}%, transparent)`,\n ...theme.applyStyles('dark', {backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.dark}%, transparent)`}),\n }, accentTextStylesOf(theme));\n}\n\n/**\n * Una pestaña: liga a su pantalla o, con submenú, botón que abre el menú de acciones.\n *\n * @param {object} props\n * @param {string} props.label ya traducido\n * @param {import('react').ElementType} [props.Icon]\n * @param {boolean} props.isActive\n * @param {{content: number|string, isCount: boolean}|null} props.badge\n * @param {object} [props.linkProps] {component, to} si navega\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.hasPopup] abre un menú\n */\nfunction TabButton({label, Icon, isActive, badge, linkProps = {}, onClick, hasPopup = false}) {\n return (\n <ButtonBase {...linkProps} onClick={onClick} aria-haspopup={hasPopup ? 'menu' : undefined} aria-current={isActive ? 'page' : undefined}\n sx={(theme) => mergeStyles({\n flex: '1 1 0', minWidth: 0, height: '100%', borderRadius: '999px', px: 0,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 0.25,\n color: 'text.primary',\n transition: theme.transitions.create(['background-color', 'color'], {duration: theme.transitions.duration.shortest}),\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {flexDirection: 'row', gap: 0.75},\n }, isActive ? activeTabOf(theme) : {})}>\n <Badge invisible={!badge?.isCount} color=\"error\" max={BADGE_MAX} overlap=\"circular\" badgeContent={badge?.isCount ? badge.content : undefined}>\n {Icon ? <Icon sx={{fontSize: TAB_ICON_SIZE}} /> : <Box sx={{width: TAB_ICON_SIZE, height: TAB_ICON_SIZE}} />}\n </Badge>\n <Typography component=\"span\" noWrap\n sx={{...textSizeOf(TAB_LABEL_PIXELS.portrait), fontWeight: 500, maxWidth: '100%',\n [PHONE_LANDSCAPE_MEDIA_QUERY]: textSizeOf(TAB_LABEL_PIXELS.landscape)}}>\n {label}\n </Typography>\n </ButtonBase>\n );\n}\n\n/**\n * Cuántas pestañas caben en la cápsula (contando «Más»). Se mide la cápsula, que no cambia de ancho con sus pestañas (se\n * estira hasta su tope), así que la cuenta no da vueltas. Se vuelve a medir al girar el teléfono o cambiar la ventana.\n *\n * @param {HTMLElement|null} capsule\n * @param {number} minTabPixels lo menos que mide una pestaña\n * @returns {number}\n */\nfunction useFittingTabCount(capsule, minTabPixels) {\n const [tabCount, setTabCount] = useState(DEFAULT_TAB_COUNT);\n // antes de pintar: así no se ve un momento con las pestañas apretadas\n useLayoutEffect(() => {\n if (!capsule) {\n return undefined;\n }\n const measure = () => {\n const style = window.getComputedStyle(capsule);\n const innerWidth = capsule.clientWidth - Number.parseFloat(style.paddingLeft) - Number.parseFloat(style.paddingRight);\n // en escritorio la barra no se ve (ancho 0): se quedan las de siempre\n if (innerWidth > 0) {\n setTabCount(Math.max(2, Math.floor(innerWidth / minTabPixels)));\n }\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(capsule);\n return () => observer.disconnect();\n }, [capsule, minTabPixels]);\n return tabCount;\n}\n\n/**\n * @param {object} props\n * @param {object[]} props.menuItems lo que regresa composeMenu\n * @param {object|null} props.activeItem la entrada de la pantalla abierta\n * @param {Record<string, number>} [props.menuBadges] los conteos del servidor por badgeKey\n * @param {import('react').ReactNode} [props.account] la cuenta (o el engrane), en su círculo junto a la barra\n * @param {import('react').ReactNode} [props.moreFooter] lo que va al final de la hoja de «Más» (la tarjeta del menú lateral)\n * @param {boolean} [props.minimizeOnScroll] se encoge al leer hacia abajo; por omisión sí\n */\nexport default function MobileTabBar({menuItems, activeItem, menuBadges, account, moreFooter, minimizeOnScroll = true}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n const {pinnedKeys, setPinnedKeys} = usePinnedMobileTabs();\n const [tabsCapsule, setTabsCapsule] = useState(null);\n const isLandscape = useMediaQuery(PHONE_LANDSCAPE_MEDIA_QUERY);\n const tabCount = useFittingTabCount(tabsCapsule, isLandscape ? MIN_TAB_PIXELS.landscape : MIN_TAB_PIXELS.portrait);\n const {tabs, more} = mobileTabsOf(menuItems, pinnedKeys, tabCount);\n const [submenu, setSubmenu] = useState({anchorElement: null, menuItem: null});\n const [isMoreOpen, setIsMoreOpen] = useState(false);\n const [isEditingTabs, setIsEditingTabs] = useState(false);\n // al salir del editor su botón desaparece con el foco adentro: el foco regresa a «Editar la barra» (sin eso se perdía\n // y Escape ya no cerraba la hoja; también lo necesita quien usa lector de pantalla)\n const [returnsFromEditor, setReturnsFromEditor] = useState(false);\n const leaveEditor = () => {\n setIsEditingTabs(false);\n setReturnsFromEditor(true);\n };\n const closeMore = () => {\n setIsMoreOpen(false);\n setIsEditingTabs(false);\n setReturnsFromEditor(false);\n };\n const {isMinimized, expand} = useTabBarMinimized(minimizeOnScroll);\n const closeSubmenu = () => setSubmenu({anchorElement: null, menuItem: null});\n const isInside = (menuItem) => menuItem === activeItem || (menuItem.children ?? []).includes(activeItem);\n const moreHasActive = more.some(isInside);\n // la cápsula chica enseña dónde está la persona: su pestaña, «Más» si está en algo de ahí, o la primera\n const activeTab = tabs.find(isInside);\n const MinimizedIcon = (!activeTab && moreHasActive ? MoreHorizRoundedIcon : (activeTab ?? tabs[0])?.Icon) ?? MoreHorizRoundedIcon;\n // «Más» suma los conteos de lo que guarda (solo conteos: los de texto no van en la pestaña)\n const moreCount = more.reduce((total, menuItem) => {\n const badge = menuBadgeOf(menuItem, menuBadges);\n return total + (badge?.isCount ? badge.content : 0);\n }, 0);\n const moreBadge = moreCount > 0 ? {content: moreCount, isCount: true} : null;\n\n const glassSurfaceSx = floatingSurfaceOf;\n\n return (\n <>\n <Box component=\"nav\" aria-label={t('drawer.ariaLabel')}\n sx={(theme) => ({\n display: 'none',\n [PHONE_MEDIA_QUERY]: {display: 'flex'},\n position: 'fixed', zIndex: theme.zIndex.appBar, gap: 0.75, justifyContent: 'center',\n left: GLASS_PANEL_GAP_PIXELS, right: GLASS_PANEL_GAP_PIXELS,\n bottom: `calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n height: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {height: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n })}>\n <Box ref={setTabsCapsule} inert={isMinimized}\n sx={(theme) => ({...glassSurfaceSx(theme), display: 'flex', flex: 1, maxWidth: TAB_BAR_MAX_WIDTH_PIXELS, p: 0.375,\n ...appearStylesOf(theme, !isMinimized, 'left bottom')})}>\n {tabs.map((menuItem) => {\n const hasChildren = (menuItem.children ?? []).length > 0;\n return (\n <TabButton key={menuItem.to ?? menuItemLabelOf(menuItem, t)} label={menuItemLabelOf(menuItem, t)} Icon={menuItem.Icon}\n isActive={isInside(menuItem)} badge={menuBadgeOf(menuItem, menuBadges)} hasPopup={hasChildren}\n linkProps={hasChildren ? {} : {component: RouterLink, to: menuItem.to}}\n onClick={hasChildren ? (event) => setSubmenu({anchorElement: event.currentTarget, menuItem}) : undefined} />\n );\n })}\n {more.length > 0 && (\n <TabButton label={t('drawer.more')} Icon={MoreHorizRoundedIcon} isActive={moreHasActive} badge={moreBadge} hasPopup\n onClick={() => setIsMoreOpen(true)} />\n )}\n </Box>\n {/* encogida: la cápsula chica en la esquina, con la pestaña de ahorita; tocarla regresa la barra completa */}\n <ButtonBase inert={!isMinimized} onClick={expand} aria-label={t('drawer.showTabs')} aria-expanded={false}\n sx={(theme) => mergeStyles({\n ...glassSurfaceSx(theme), position: 'absolute', left: 0, bottom: 0,\n width: MINIMIZED_PIXELS.portrait, height: MINIMIZED_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {width: MINIMIZED_PIXELS.landscape, height: MINIMIZED_PIXELS.landscape},\n ...appearStylesOf(theme, isMinimized, 'left bottom'),\n }, accentTextStylesOf(theme))}>\n <MinimizedIcon sx={{fontSize: TAB_ICON_SIZE}} />\n </ButtonBase>\n {account && (\n <Box sx={(theme) => ({\n ...glassSurfaceSx(theme), flexShrink: 0, minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait, px: 0.5, display: 'flex', alignItems: 'center',\n justifyContent: 'center', [PHONE_LANDSCAPE_MEDIA_QUERY]: {minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n '& .MuiButtonBase-root': {color: 'inherit'},\n // encogida, la cuenta se queda pero del alto de la cápsula chica, pegada a su esquina\n transformOrigin: 'right bottom',\n transform: isMinimized ? `scale(${MINIMIZED_PIXELS.portrait / MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait})` : 'none',\n transition: theme.transitions.create('transform', {duration: theme.transitions.duration.short}),\n ...(isMinimized && {[PHONE_LANDSCAPE_MEDIA_QUERY]: {\n minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape,\n transform: `scale(${MINIMIZED_PIXELS.landscape / MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape})`,\n }}),\n })}>\n {account}\n </Box>\n )}\n </Box>\n\n {submenu.menuItem && (\n <SubmenuActionsMenu anchorElement={submenu.anchorElement} onClose={closeSubmenu} menuItem={submenu.menuItem} activeItem={activeItem}\n menuBadges={menuBadges} placement={SUBMENU_PLACEMENT.above} />\n )}\n\n {/* «Más»: el resto del menú en una hoja desde abajo, separada de las orillas como las hojas de iOS */}\n <Drawer anchor=\"bottom\" open={isMoreOpen} onClose={closeMore}\n slotProps={{paper: {sx: {\n m: `${GLASS_PANEL_GAP_PIXELS}px auto calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n width: `min(${MORE_SHEET_MAX_WIDTH_PIXELS}px, calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px))`,\n maxHeight: MORE_SHEET_MAX_HEIGHT, borderRadius: '20px', overflow: 'hidden',\n }}}}>\n {isEditingTabs ? (\n <TabBarEditor menuItems={menuItems} pinnedKeys={tabs.map(menuItemKeyOf)}\n onDone={(keys) => {\n setPinnedKeys(keys);\n leaveEditor();\n }}\n onReset={() => {\n setPinnedKeys(null);\n leaveEditor();\n }} />\n ) : (\n <>\n <Box sx={{display: 'flex', justifyContent: 'flex-end', px: 1.5, pt: 1, flexShrink: 0}}>\n <Button size=\"small\" autoFocus={returnsFromEditor} onClick={() => setIsEditingTabs(true)}>{t('drawer.editTabs')}</Button>\n </Box>\n <SideMenu menuItems={more} activeItem={activeItem} menuBadges={menuBadges} collapsed={false} showBrand={false} footer={moreFooter}\n onNavigate={closeMore} />\n </>\n )}\n </Drawer>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,MAAM,mBAAmB,OAAO,OAAO,EAAC,UAAU,IAAI,WAAW,IAAG;AACpE,MAAM,gBAAgB;AACtB,MAAM,2BAA2B;AAGjC,MAAM,iBAAiB,OAAO,OAAO,EAAC,UAAU,IAAI,WAAW,KAAI;AAEnE,MAAM,oBAAoB;AAC1B,MAAM,8BAA8B;AACpC,MAAM,wBAAwB;AAC9B,MAAM,YAAY;AAGlB,MAAM,yBAAyB;AAC/B,MAAM,gBAAgB,OAAO,OAAO;AAAA,EAChC,OAAO,EAAC,SAAS,0BAA0B,QAAQ,uEAAA;AAAA,EACnD,MAAM,EAAC,SAAS,6BAA6B,QAAQ,gEAAA;AACzD,CAAC;AAED,MAAM,sBAAsB,OAAO,OAAO,EAAC,OAAO,IAAI,MAAM,IAAG;AAE/D,MAAM,mBAAmB,OAAO,OAAO,EAAC,UAAU,IAAI,WAAW,IAAG;AACpE,MAAM,eAAe;AAWrB,SAAS,eAAe,OAAO,SAAS,iBAAiB;AACrD,QAAM,WAAW,MAAM,YAAY,SAAS;AAC5C,SAAO;AAAA,IACH;AAAA,IACA,SAAS,UAAU,IAAI;AAAA,IACvB,WAAW,UAAU,SAAS,SAAS,YAAY;AAAA,IACnD,YAAY,UAAU,YAAY;AAAA,IAClC,YAAY,GAAG,MAAM,YAAY,OAAO,CAAC,WAAW,WAAW,GAAG,EAAC,UAAS,CAAC,0BAA0B,UAAU,IAAI,QAAQ;AAAA,EAAA;AAErI;AASA,SAAS,kBAAkB,OAAO;AAE9B,MAAI,CAAC,aAAa,KAAK,GAAG;AACtB,WAAO,EAAC,GAAG,wBAAwB,KAAK,GAAG,UAAU,YAAY,cAAc,SAAS,QAAQ,OAAA;AAAA,EACpG;AACA,SAAO;AAAA,IACH,GAAG,cAAc,OAAO,EAAC,SAAS,wBAAuB;AAAA,IACzD,UAAU;AAAA,IACV,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,SAAS,aAAa,cAAc,MAAM,OAAO;AAAA,IACjD,aAAa;AAAA,MACT,SAAS;AAAA,MAAM,UAAU;AAAA,MAAY,OAAO;AAAA,MAAG,cAAc;AAAA,MAAW,QAAQ;AAAA,MAAI,eAAe;AAAA,MACnG,WAAW,cAAc,MAAM;AAAA,IAAA;AAAA,IAEnC,GAAG,MAAM,YAAY,QAAQ;AAAA,MACzB,SAAS,aAAa,cAAc,KAAK,OAAO;AAAA,MAChD,aAAa,EAAC,WAAW,cAAc,KAAK,OAAA;AAAA,IAAM,CACrD;AAAA,EAAA;AAET;AAQA,SAAS,YAAY,OAAO;AACxB,QAAM,WAAW,MAAM,QAAQ,OAAO,QAAQ,QAAQ;AACtD,SAAO,YAAY;AAAA,IACf,iBAAiB,sBAAsB,OAAO,IAAI,oBAAoB,KAAK;AAAA,IAC3E,GAAG,MAAM,YAAY,QAAQ,EAAC,iBAAiB,sBAAsB,OAAO,IAAI,oBAAoB,IAAI,kBAAA,CAAkB;AAAA,EAAA,GAC3H,mBAAmB,KAAK,CAAC;AAChC;AAcA,SAAS,UAAU,EAAC,OAAO,MAAM,UAAU,OAAO,YAAY,IAAI,SAAS,WAAW,MAAA,GAAQ;AAC1F,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAY,GAAG;AAAA,MAAW;AAAA,MAAkB,iBAAe,WAAW,SAAS;AAAA,MAAW,gBAAc,WAAW,SAAS;AAAA,MACjH,IAAI,CAAC,UAAU,YAAY;AAAA,QACvB,MAAM;AAAA,QAAS,UAAU;AAAA,QAAG,QAAQ;AAAA,QAAQ,cAAc;AAAA,QAAS,IAAI;AAAA,QACvE,SAAS;AAAA,QAAQ,eAAe;AAAA,QAAU,YAAY;AAAA,QAAU,gBAAgB;AAAA,QAAU,KAAK;AAAA,QAC/F,OAAO;AAAA,QACP,YAAY,MAAM,YAAY,OAAO,CAAC,oBAAoB,OAAO,GAAG,EAAC,UAAU,MAAM,YAAY,SAAS,UAAS;AAAA,QACnH,CAAC,2BAA2B,GAAG,EAAC,eAAe,OAAO,KAAK,KAAA;AAAA,MAAI,GAChE,WAAW,YAAY,KAAK,IAAI,CAAA,CAAE;AAAA,MAC7C,UAAA;AAAA,QAAA,oBAAC,OAAA,EAAM,WAAW,CAAC,OAAO,SAAS,OAAM,SAAQ,KAAK,WAAW,SAAQ,YAAW,cAAc,OAAO,UAAU,MAAM,UAAU,QAC9H,UAAA,OAAO,oBAAC,QAAK,IAAI,EAAC,UAAU,cAAA,GAAgB,IAAK,oBAAC,KAAA,EAAI,IAAI,EAAC,OAAO,eAAe,QAAQ,cAAA,GAAgB,GAC9G;AAAA,QACA;AAAA,UAAC;AAAA,UAAA;AAAA,YAAW,WAAU;AAAA,YAAO,QAAM;AAAA,YACvB,IAAI;AAAA,cAAC,GAAG,WAAW,iBAAiB,QAAQ;AAAA,cAAG,YAAY;AAAA,cAAK,UAAU;AAAA,cACrE,CAAC,2BAA2B,GAAG,WAAW,iBAAiB,SAAS;AAAA,YAAA;AAAA,YAChF,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACL;AAAA,IAAA;AAAA,EAAA;AAGZ;AAUA,SAAS,mBAAmB,SAAS,cAAc;AAC/C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,iBAAiB;AAE1D,kBAAgB,MAAM;AAClB,QAAI,CAAC,SAAS;AACV,aAAO;AAAA,IACX;AACA,UAAM,UAAU,MAAM;AAClB,YAAM,QAAQ,OAAO,iBAAiB,OAAO;AAC7C,YAAM,aAAa,QAAQ,cAAc,OAAO,WAAW,MAAM,WAAW,IAAI,OAAO,WAAW,MAAM,YAAY;AAEpH,UAAI,aAAa,GAAG;AAChB,oBAAY,KAAK,IAAI,GAAG,KAAK,MAAM,aAAa,YAAY,CAAC,CAAC;AAAA,MAClE;AAAA,IACJ;AACA,YAAA;AACA,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,OAAO;AACxB,WAAO,MAAM,SAAS,WAAA;AAAA,EAC1B,GAAG,CAAC,SAAS,YAAY,CAAC;AAC1B,SAAO;AACX;AAWA,SAAwB,aAAa,EAAC,WAAW,YAAY,YAAY,SAAS,YAAY,mBAAmB,QAAO;AACpH,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,EAAC,MAAM,WAAA,IAAc,qBAAA;AAC3B,QAAM,EAAC,YAAY,cAAA,IAAiB,oBAAA;AACpC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,IAAI;AACnD,QAAM,cAAc,cAAc,2BAA2B;AAC7D,QAAM,WAAW,mBAAmB,aAAa,cAAc,eAAe,YAAY,eAAe,QAAQ;AACjH,QAAM,EAAC,MAAM,KAAA,IAAQ,aAAa,WAAW,YAAY,QAAQ;AACjE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAC,eAAe,MAAM,UAAU,MAAK;AAC5E,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AAGxD,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,KAAK;AAChE,QAAM,cAAc,MAAM;AACtB,qBAAiB,KAAK;AACtB,yBAAqB,IAAI;AAAA,EAC7B;AACA,QAAM,YAAY,MAAM;AACpB,kBAAc,KAAK;AACnB,qBAAiB,KAAK;AACtB,yBAAqB,KAAK;AAAA,EAC9B;AACA,QAAM,EAAC,aAAa,WAAU,mBAAmB,gBAAgB;AACjE,QAAM,eAAe,MAAM,WAAW,EAAC,eAAe,MAAM,UAAU,MAAK;AAC3E,QAAM,WAAW,CAAC,aAAa,aAAa,eAAe,SAAS,YAAY,CAAA,GAAI,SAAS,UAAU;AACvG,QAAM,gBAAgB,KAAK,KAAK,QAAQ;AAExC,QAAM,YAAY,KAAK,KAAK,QAAQ;AACpC,QAAM,iBAAiB,CAAC,aAAa,gBAAgB,wBAAwB,aAAa,KAAK,CAAC,IAAI,SAAS;AAE7G,QAAM,YAAY,KAAK,OAAO,CAAC,OAAO,aAAa;AAC/C,UAAM,QAAQ,YAAY,UAAU,UAAU;AAC9C,WAAO,SAAS,OAAO,UAAU,MAAM,UAAU;AAAA,EACrD,GAAG,CAAC;AACJ,QAAM,YAAY,YAAY,IAAI,EAAC,SAAS,WAAW,SAAS,SAAQ;AAExE,QAAM,iBAAiB;AAEvB,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAI,WAAU;AAAA,QAAM,cAAY,EAAE,kBAAkB;AAAA,QAChD,IAAI,CAAC,WAAW;AAAA,UACZ,SAAS;AAAA,UACT,CAAC,iBAAiB,GAAG,EAAC,SAAS,OAAA;AAAA,UAC/B,UAAU;AAAA,UAAS,QAAQ,MAAM,OAAO;AAAA,UAAQ,KAAK;AAAA,UAAM,gBAAgB;AAAA,UAC3E,MAAM;AAAA,UAAwB,OAAO;AAAA,UACrC,QAAQ,QAAQ,sBAAsB;AAAA,UACtC,QAAQ,6BAA6B;AAAA,UACrC,CAAC,2BAA2B,GAAG,EAAC,QAAQ,6BAA6B,UAAA;AAAA,QAAS;AAAA,QAEnF,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cAAI,KAAK;AAAA,cAAgB,OAAO;AAAA,cAC5B,IAAI,CAAC,WAAW;AAAA,gBAAC,GAAG,eAAe,KAAK;AAAA,gBAAG,SAAS;AAAA,gBAAQ,MAAM;AAAA,gBAAG,UAAU;AAAA,gBAA0B,GAAG;AAAA,gBAC3F,GAAG,eAAe,OAAO,CAAC,aAAa,aAAa;AAAA,cAAA;AAAA,cACrE,UAAA;AAAA,gBAAA,KAAK,IAAI,CAAC,aAAa;AACpB,wBAAM,eAAe,SAAS,YAAY,CAAA,GAAI,SAAS;AACvD,yBACI;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBAA4D,OAAO,gBAAgB,UAAU,CAAC;AAAA,sBAAG,MAAM,SAAS;AAAA,sBACtG,UAAU,SAAS,QAAQ;AAAA,sBAAG,OAAO,YAAY,UAAU,UAAU;AAAA,sBAAG,UAAU;AAAA,sBAClF,WAAW,cAAc,CAAA,IAAK,EAAC,WAAW,YAAY,IAAI,SAAS,GAAA;AAAA,sBACnE,SAAS,cAAc,CAAC,UAAU,WAAW,EAAC,eAAe,MAAM,eAAe,SAAA,CAAS,IAAI;AAAA,oBAAA;AAAA,oBAH1F,SAAS,MAAM,gBAAgB,UAAU,CAAC;AAAA,kBAAA;AAAA,gBAKlE,CAAC;AAAA,gBACA,KAAK,SAAS,KACX;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAAU,OAAO,EAAE,aAAa;AAAA,oBAAG,MAAM;AAAA,oBAAsB,UAAU;AAAA,oBAAe,OAAO;AAAA,oBAAW,UAAQ;AAAA,oBACxG,SAAS,MAAM,cAAc,IAAI;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAG;AAAA,YAAA;AAAA,UAAA;AAAA,UAIvD;AAAA,YAAC;AAAA,YAAA;AAAA,cAAW,OAAO,CAAC;AAAA,cAAa,SAAS;AAAA,cAAQ,cAAY,EAAE,iBAAiB;AAAA,cAAG,iBAAe;AAAA,cACvF,IAAI,CAAC,UAAU,YAAY;AAAA,gBACvB,GAAG,eAAe,KAAK;AAAA,gBAAG,UAAU;AAAA,gBAAY,MAAM;AAAA,gBAAG,QAAQ;AAAA,gBACjE,OAAO,iBAAiB;AAAA,gBAAU,QAAQ,iBAAiB;AAAA,gBAC3D,CAAC,2BAA2B,GAAG,EAAC,OAAO,iBAAiB,WAAW,QAAQ,iBAAiB,UAAA;AAAA,gBAC5F,GAAG,eAAe,OAAO,aAAa,aAAa;AAAA,cAAA,GACpD,mBAAmB,KAAK,CAAC;AAAA,cACpC,8BAAC,eAAA,EAAc,IAAI,EAAC,UAAU,gBAAa,CAAG;AAAA,YAAA;AAAA,UAAA;AAAA,UAEjD,WACG,oBAAC,KAAA,EAAI,IAAI,CAAC,WAAW;AAAA,YACjB,GAAG,eAAe,KAAK;AAAA,YAAG,YAAY;AAAA,YAAG,UAAU,6BAA6B;AAAA,YAAU,IAAI;AAAA,YAAK,SAAS;AAAA,YAAQ,YAAY;AAAA,YAChI,gBAAgB;AAAA,YAAU,CAAC,2BAA2B,GAAG,EAAC,UAAU,6BAA6B,UAAA;AAAA,YACjG,yBAAyB,EAAC,OAAO,UAAA;AAAA;AAAA,YAEjC,iBAAiB;AAAA,YACjB,WAAW,cAAc,SAAS,iBAAiB,WAAW,6BAA6B,QAAQ,MAAM;AAAA,YACzG,YAAY,MAAM,YAAY,OAAO,aAAa,EAAC,UAAU,MAAM,YAAY,SAAS,MAAA,CAAM;AAAA,YAC9F,GAAI,eAAe,EAAC,CAAC,2BAA2B,GAAG;AAAA,cAC/C,UAAU,6BAA6B;AAAA,cACvC,WAAW,SAAS,iBAAiB,YAAY,6BAA6B,SAAS;AAAA,YAAA,EAC3F;AAAA,UAAC,IAEA,UAAA,QAAA,CACL;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAIP,QAAQ,YACL;AAAA,MAAC;AAAA,MAAA;AAAA,QAAmB,eAAe,QAAQ;AAAA,QAAe,SAAS;AAAA,QAAc,UAAU,QAAQ;AAAA,QAAU;AAAA,QACzF;AAAA,QAAwB,WAAW,kBAAkB;AAAA,MAAA;AAAA,IAAA;AAAA,IAI7E;AAAA,MAAC;AAAA,MAAA;AAAA,QAAO,QAAO;AAAA,QAAS,MAAM;AAAA,QAAY,SAAS;AAAA,QAC3C,WAAW,EAAC,OAAO,EAAC,IAAI;AAAA,UACpB,GAAG,GAAG,sBAAsB,gBAAgB,sBAAsB;AAAA,UAClE,OAAO,OAAO,2BAA2B,mBAAmB,yBAAyB,CAAC;AAAA,UACtF,WAAW;AAAA,UAAuB,cAAc;AAAA,UAAQ,UAAU;AAAA,QAAA,IACrE;AAAA,QACJ,UAAA,gBACG;AAAA,UAAC;AAAA,UAAA;AAAA,YAAa;AAAA,YAAsB,YAAY,KAAK,IAAI,aAAa;AAAA,YACxD,QAAQ,CAAC,SAAS;AACd,4BAAc,IAAI;AAClB,0BAAA;AAAA,YACJ;AAAA,YACA,SAAS,MAAM;AACX,4BAAc,IAAI;AAClB,0BAAA;AAAA,YACJ;AAAA,UAAA;AAAA,QAAA,IAEd,qBAAA,UAAA,EACI,UAAA;AAAA,UAAA,oBAAC,KAAA,EAAI,IAAI,EAAC,SAAS,QAAQ,gBAAgB,YAAY,IAAI,KAAK,IAAI,GAAG,YAAY,EAAA,GAC/E,UAAA,oBAAC,QAAA,EAAO,MAAK,SAAQ,WAAW,mBAAmB,SAAS,MAAM,iBAAiB,IAAI,GAAI,UAAA,EAAE,iBAAiB,EAAA,CAAE,GACpH;AAAA,UACA;AAAA,YAAC;AAAA,YAAA;AAAA,cAAS,WAAW;AAAA,cAAM;AAAA,cAAwB;AAAA,cAAwB,WAAW;AAAA,cAAO,WAAW;AAAA,cAAO,QAAQ;AAAA,cAC7G,YAAY;AAAA,YAAA;AAAA,UAAA;AAAA,QAAW,EAAA,CACrC;AAAA,MAAA;AAAA,IAAA;AAAA,EAER,GACJ;AAER;"}
|
|
1
|
+
{"version":3,"file":"MobileTabBar.js","sources":["../../src/appShell/MobileTabBar.jsx"],"sourcesContent":["// La barra de pestañas del teléfono con vidrio, como la de iOS 26: una cápsula de vidrio flotando abajo con las secciones\n// principales (mobileTabsOf: las que caben a lo ancho, y «Más» con el resto), y aparte, en su círculo de vidrio, la cuenta\n// (con todos los ajustes adentro). La reemplaza a la barra de abajo con ☰ y al cajón del menú (lo pidió el autor: «estilo\n// Apple pero con sus submenús de acciones»). También va con el minimalista cuando la barra va en cápsulas (usesMobileTabBar), sólida y\n// del gris de lo que flota (minimalFloatingStylesOf).\n//\n// - Una pestaña con submenú no navega: abre el menú de acciones de Apple arriba de ella (SubmenuActionsMenu).\n// - «Más» abre una hoja desde abajo con el resto del menú, por secciones (SideMenu, el mismo del escritorio). Arriba trae\n// «Editar la barra»: cada quien elige qué pantallas van abajo (TabBarEditor; se recuerda con usePinnedMobileTabs),\n// como pide la guía de Apple. Sin elegir, van las que marca el proyecto (mobileTab en menu.js).\n// - Cada pestaña lleva su badge como los de Apple (HIG «Tab bars»): ROJO con el número en blanco y solo para conteos (lo\n// pendiente, del servidor); un badge de texto («Nuevo») no va en la pestaña, se queda en el menú: Apple los reserva para\n// lo que pide atención.\n// - Cuántas pestañas van se mide (useFittingTabCount): las que caben con al menos MIN_TAB_PIXELS cada una; las demás van\n// a «Más». En una ventana angosta salen menos y en una ancha más, y los nombres no se cortan («Compone…»).\n// - Acostado (pantalla bajita), la barra va compacta: ícono y nombre lado a lado, para dejarle el alto al contenido.\n// - Al leer hacia abajo se encoge (como iOS 26): queda una cápsula chica con la pestaña donde está la persona, y la cuenta\n// más chica; al subir un poco, o al tocar la cápsula, regresa completa (cuándo: tabBarScroll.js). Se mueve solo con\n// opacidad y escala, lo que el navegador anima sin volver a acomodar la página: en un teléfono barato no se traba.\n// Solo se ve en el teléfono (PHONE_MEDIA_QUERY); en escritorio no ocupa nada.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useLayoutEffect, useState} from 'react';\nimport Badge from '@mui/material/Badge';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Drawer from '@mui/material/Drawer';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\nimport {menuItemLabelOf} from '@/appShell/menuItemText.js';\nimport {\n GLASS_PANEL_GAP_PIXELS, MOBILE_TAB_BAR_HEIGHT_PIXELS, PHONE_LANDSCAPE_MEDIA_QUERY, PHONE_MEDIA_QUERY,\n} from '@/appShell/shellSizes.js';\nimport SideMenu from '@/appShell/SideMenu.jsx';\nimport SubmenuActionsMenu, {SUBMENU_PLACEMENT} from '@/appShell/SubmenuActionsMenu.jsx';\nimport TabBarEditor from '@/appShell/TabBarEditor.jsx';\nimport {usePinnedMobileTabs} from '@/appShell/usePinnedMobileTabs.js';\nimport {useTabBarMinimized} from '@/appShell/useTabBarMinimized.js';\nimport {menuBadgeOf, menuItemKeyOf, mobileTabsOf} from '@/navigation/appMenu.js';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {accentTextStylesOf, glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {mergeStyles} from '@/theme/mergeComponents.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n// el nombre de la pestaña parado y acostado (px), lo ancho máximo de la cápsula y lo alto de la hoja de «Más»\nconst TAB_LABEL_PIXELS = Object.freeze({portrait: 10, landscape: 12});\nconst TAB_ICON_SIZE = '1.5rem';\nconst TAB_BAR_MAX_WIDTH_PIXELS = 560;\n// lo menos que mide una pestaña (px) para que su nombre se lea entero: parada, un nombre de unas 11 letras («Formularios»\n// pide 61 px, medido en Chrome); acostada, ícono y nombre lado a lado. Con menos, la pestaña se va a «Más»\nconst MIN_TAB_PIXELS = Object.freeze({portrait: 60, landscape: 104});\n// antes de medir (o si la barra no se ve): las de siempre, 4 y «Más»\nconst DEFAULT_TAB_COUNT = 5;\nconst MORE_SHEET_MAX_WIDTH_PIXELS = 640;\nconst MORE_SHEET_MAX_HEIGHT = '75vh';\nconst BADGE_MAX = 99;\n// la barra flota sobre el contenido: vidrio más opaco que el de los controles, una línea fina en la orilla y una sombra\n// debajo. Con el vidrio de siempre, en claro se perdía sobre las tarjetas blancas y no parecía flotar (lo reportaron)\nconst FLOATING_GLASS_OPACITY = 0.8;\nconst FLOATING_EDGE = Object.freeze({\n light: {outline: 'rgba(15, 23, 42, 0.12)', shadow: '0 10px 30px rgba(15, 23, 42, 0.20), 0 2px 6px rgba(15, 23, 42, 0.10)'},\n dark: {outline: 'rgba(255, 255, 255, 0.16)', shadow: '0 10px 30px rgba(0, 0, 0, 0.65), 0 2px 6px rgba(0, 0, 0, 0.4)'},\n});\n// la pestaña elegida: un tinte del acento (en claro una gota blanca sobre la cápsula blanca no se distinguía)\nconst ACTIVE_TINT_PERCENT = Object.freeze({light: 14, dark: 24});\n// encogida: lo alto de la cápsula chica (px) y a qué tamaño se va lo que desaparece\nconst MINIMIZED_PIXELS = Object.freeze({portrait: 48, landscape: 40});\nconst HIDDEN_SCALE = 0.6;\n\n/**\n * Lo que aparece o desaparece al encoger la barra: solo opacidad y escala. visibility espera a que termine la animación\n * para esconderlo, y así lo que ya no se ve tampoco se puede tocar.\n *\n * @param {object} theme\n * @param {boolean} isShown\n * @param {string} transformOrigin hacia dónde se encoge ('left bottom')\n * @returns {object}\n */\nfunction appearStylesOf(theme, isShown, transformOrigin) {\n const duration = theme.transitions.duration.short;\n return {\n transformOrigin,\n opacity: isShown ? 1 : 0,\n transform: isShown ? 'none' : `scale(${HIDDEN_SCALE})`,\n visibility: isShown ? 'visible' : 'hidden',\n transition: `${theme.transitions.create(['opacity', 'transform'], {duration})}, visibility 0s linear ${isShown ? 0 : duration}ms`,\n };\n}\n\n/**\n * La superficie de la barra y del círculo de la cuenta: vidrio más opaco, su línea fina y su sombra (la sombra va en una\n * capa detrás, ::before, para no pisar el filo de luz del vidrio, que también es una sombra). Con el minimalista, sólida.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction floatingSurfaceOf(theme) {\n // con el minimalista (barra en cápsulas) la barra es sólida: su gris de lo que flota, sin línea ni sombra\n if (!isGlassTheme(theme)) {\n return {...minimalFloatingStylesOf(theme), position: 'relative', borderRadius: '999px', height: '100%'};\n }\n return {\n ...glassStylesOf(theme, {opacity: FLOATING_GLASS_OPACITY}),\n position: 'relative',\n borderRadius: '999px',\n height: '100%',\n outline: `1px solid ${FLOATING_EDGE.light.outline}`,\n '&::before': {\n content: '\"\"', position: 'absolute', inset: 0, borderRadius: 'inherit', zIndex: -1, pointerEvents: 'none',\n boxShadow: FLOATING_EDGE.light.shadow,\n },\n ...theme.applyStyles('dark', {\n outline: `1px solid ${FLOATING_EDGE.dark.outline}`,\n '&::before': {boxShadow: FLOATING_EDGE.dark.shadow},\n }),\n };\n}\n\n/**\n * La pestaña elegida: el tinte del acento y su letra legible.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction activeTabOf(theme) {\n const primary = (theme.vars ?? theme).palette.primary.main;\n return mergeStyles({\n backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.light}%, transparent)`,\n ...theme.applyStyles('dark', {backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.dark}%, transparent)`}),\n }, accentTextStylesOf(theme));\n}\n\n/**\n * Una pestaña: liga a su pantalla o, con submenú, botón que abre el menú de acciones.\n *\n * @param {object} props\n * @param {string} props.label ya traducido\n * @param {import('react').ElementType} [props.Icon]\n * @param {boolean} props.isActive\n * @param {{content: number|string, isCount: boolean}|null} props.badge\n * @param {object} [props.linkProps] {component, to} si navega\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.hasPopup] abre un menú\n */\nfunction TabButton({label, Icon, isActive, badge, linkProps = {}, onClick, hasPopup = false}) {\n return (\n <ButtonBase {...linkProps} onClick={onClick} aria-haspopup={hasPopup ? 'menu' : undefined} aria-current={isActive ? 'page' : undefined}\n sx={(theme) => mergeStyles({\n flex: '1 1 0', minWidth: 0, height: '100%', borderRadius: '999px', px: 0,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 0.25,\n color: 'text.primary',\n transition: theme.transitions.create(['background-color', 'color'], {duration: theme.transitions.duration.shortest}),\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {flexDirection: 'row', gap: 0.75},\n }, isActive ? activeTabOf(theme) : {})}>\n <Badge invisible={!badge?.isCount} color=\"error\" max={BADGE_MAX} overlap=\"circular\" badgeContent={badge?.isCount ? badge.content : undefined}>\n {Icon ? <Icon sx={{fontSize: TAB_ICON_SIZE}} /> : <Box sx={{width: TAB_ICON_SIZE, height: TAB_ICON_SIZE}} />}\n </Badge>\n <Typography component=\"span\" noWrap\n sx={{...textSizeOf(TAB_LABEL_PIXELS.portrait), fontWeight: 500, maxWidth: '100%',\n [PHONE_LANDSCAPE_MEDIA_QUERY]: textSizeOf(TAB_LABEL_PIXELS.landscape)}}>\n {label}\n </Typography>\n </ButtonBase>\n );\n}\n\n/**\n * Cuántas pestañas caben en la cápsula (contando «Más»). Se mide la cápsula, que no cambia de ancho con sus pestañas (se\n * estira hasta su tope), así que la cuenta no da vueltas. Se vuelve a medir al girar el teléfono o cambiar la ventana.\n *\n * @param {HTMLElement|null} capsule\n * @param {number} minTabPixels lo menos que mide una pestaña\n * @returns {number}\n */\nfunction useFittingTabCount(capsule, minTabPixels) {\n const [tabCount, setTabCount] = useState(DEFAULT_TAB_COUNT);\n // antes de pintar: así no se ve un momento con las pestañas apretadas\n useLayoutEffect(() => {\n if (!capsule) {\n return undefined;\n }\n const measure = () => {\n const style = window.getComputedStyle(capsule);\n const innerWidth = capsule.clientWidth - Number.parseFloat(style.paddingLeft) - Number.parseFloat(style.paddingRight);\n // en escritorio la barra no se ve (ancho 0): se quedan las de siempre\n if (innerWidth > 0) {\n setTabCount(Math.max(2, Math.floor(innerWidth / minTabPixels)));\n }\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(capsule);\n return () => observer.disconnect();\n }, [capsule, minTabPixels]);\n return tabCount;\n}\n\n/**\n * @param {object} props\n * @param {object[]} props.menuItems lo que regresa composeMenu\n * @param {object|null} props.activeItem la entrada de la pantalla abierta\n * @param {Record<string, number>} [props.menuBadges] los conteos del servidor por badgeKey\n * @param {import('react').ReactNode} [props.account] la cuenta (o el engrane), en su círculo junto a la barra\n * @param {import('react').ReactNode} [props.moreFooter] lo que va al final de la hoja de «Más» (la tarjeta del menú lateral)\n * @param {boolean} [props.minimizeOnScroll] se encoge al leer hacia abajo; por omisión sí\n * @param {(keys: string[]|null) => void} [props.onPinnedTabsChange] la persona eligió sus pestañas (null: regresó a las de\n * omisión)\n */\nexport default function MobileTabBar({menuItems, activeItem, menuBadges, account, moreFooter, minimizeOnScroll = true, onPinnedTabsChange}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n const {pinnedKeys, setPinnedKeys} = usePinnedMobileTabs();\n const [tabsCapsule, setTabsCapsule] = useState(null);\n const isLandscape = useMediaQuery(PHONE_LANDSCAPE_MEDIA_QUERY);\n const tabCount = useFittingTabCount(tabsCapsule, isLandscape ? MIN_TAB_PIXELS.landscape : MIN_TAB_PIXELS.portrait);\n const {tabs, more} = mobileTabsOf(menuItems, pinnedKeys, tabCount);\n const [submenu, setSubmenu] = useState({anchorElement: null, menuItem: null});\n const [isMoreOpen, setIsMoreOpen] = useState(false);\n const [isEditingTabs, setIsEditingTabs] = useState(false);\n // al salir del editor su botón desaparece con el foco adentro: el foco regresa a «Editar la barra» (sin eso se perdía\n // y Escape ya no cerraba la hoja; también lo necesita quien usa lector de pantalla)\n const [returnsFromEditor, setReturnsFromEditor] = useState(false);\n const leaveEditor = () => {\n setIsEditingTabs(false);\n setReturnsFromEditor(true);\n };\n const closeMore = () => {\n setIsMoreOpen(false);\n setIsEditingTabs(false);\n setReturnsFromEditor(false);\n };\n const {isMinimized, expand} = useTabBarMinimized(minimizeOnScroll);\n const closeSubmenu = () => setSubmenu({anchorElement: null, menuItem: null});\n const isInside = (menuItem) => menuItem === activeItem || (menuItem.children ?? []).includes(activeItem);\n const moreHasActive = more.some(isInside);\n // la cápsula chica enseña dónde está la persona: su pestaña, «Más» si está en algo de ahí, o la primera\n const activeTab = tabs.find(isInside);\n const MinimizedIcon = (!activeTab && moreHasActive ? MoreHorizRoundedIcon : (activeTab ?? tabs[0])?.Icon) ?? MoreHorizRoundedIcon;\n // «Más» suma los conteos de lo que guarda (solo conteos: los de texto no van en la pestaña)\n const moreCount = more.reduce((total, menuItem) => {\n const badge = menuBadgeOf(menuItem, menuBadges);\n return total + (badge?.isCount ? badge.content : 0);\n }, 0);\n const moreBadge = moreCount > 0 ? {content: moreCount, isCount: true} : null;\n\n const glassSurfaceSx = floatingSurfaceOf;\n\n return (\n <>\n <Box component=\"nav\" aria-label={t('drawer.ariaLabel')}\n sx={(theme) => ({\n display: 'none',\n [PHONE_MEDIA_QUERY]: {display: 'flex'},\n position: 'fixed', zIndex: theme.zIndex.appBar, gap: 0.75, justifyContent: 'center',\n left: GLASS_PANEL_GAP_PIXELS, right: GLASS_PANEL_GAP_PIXELS,\n bottom: `calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n height: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {height: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n })}>\n <Box ref={setTabsCapsule} inert={isMinimized}\n sx={(theme) => ({...glassSurfaceSx(theme), display: 'flex', flex: 1, maxWidth: TAB_BAR_MAX_WIDTH_PIXELS, p: 0.375,\n ...appearStylesOf(theme, !isMinimized, 'left bottom')})}>\n {tabs.map((menuItem) => {\n const hasChildren = (menuItem.children ?? []).length > 0;\n return (\n <TabButton key={menuItem.to ?? menuItemLabelOf(menuItem, t)} label={menuItemLabelOf(menuItem, t)} Icon={menuItem.Icon}\n isActive={isInside(menuItem)} badge={menuBadgeOf(menuItem, menuBadges)} hasPopup={hasChildren}\n linkProps={hasChildren ? {} : {component: RouterLink, to: menuItem.to}}\n onClick={hasChildren ? (event) => setSubmenu({anchorElement: event.currentTarget, menuItem}) : undefined} />\n );\n })}\n {more.length > 0 && (\n <TabButton label={t('drawer.more')} Icon={MoreHorizRoundedIcon} isActive={moreHasActive} badge={moreBadge} hasPopup\n onClick={() => setIsMoreOpen(true)} />\n )}\n </Box>\n {/* encogida: la cápsula chica en la esquina, con la pestaña de ahorita; tocarla regresa la barra completa */}\n <ButtonBase inert={!isMinimized} onClick={expand} aria-label={t('drawer.showTabs')} aria-expanded={false}\n sx={(theme) => mergeStyles({\n ...glassSurfaceSx(theme), position: 'absolute', left: 0, bottom: 0,\n width: MINIMIZED_PIXELS.portrait, height: MINIMIZED_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {width: MINIMIZED_PIXELS.landscape, height: MINIMIZED_PIXELS.landscape},\n ...appearStylesOf(theme, isMinimized, 'left bottom'),\n }, accentTextStylesOf(theme))}>\n <MinimizedIcon sx={{fontSize: TAB_ICON_SIZE}} />\n </ButtonBase>\n {account && (\n <Box sx={(theme) => ({\n ...glassSurfaceSx(theme), flexShrink: 0, minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait, px: 0.5, display: 'flex', alignItems: 'center',\n justifyContent: 'center', [PHONE_LANDSCAPE_MEDIA_QUERY]: {minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n '& .MuiButtonBase-root': {color: 'inherit'},\n // encogida, la cuenta se queda pero del alto de la cápsula chica, pegada a su esquina\n transformOrigin: 'right bottom',\n transform: isMinimized ? `scale(${MINIMIZED_PIXELS.portrait / MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait})` : 'none',\n transition: theme.transitions.create('transform', {duration: theme.transitions.duration.short}),\n ...(isMinimized && {[PHONE_LANDSCAPE_MEDIA_QUERY]: {\n minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape,\n transform: `scale(${MINIMIZED_PIXELS.landscape / MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape})`,\n }}),\n })}>\n {account}\n </Box>\n )}\n </Box>\n\n {submenu.menuItem && (\n <SubmenuActionsMenu anchorElement={submenu.anchorElement} onClose={closeSubmenu} menuItem={submenu.menuItem} activeItem={activeItem}\n menuBadges={menuBadges} placement={SUBMENU_PLACEMENT.above} />\n )}\n\n {/* «Más»: el resto del menú en una hoja desde abajo, separada de las orillas como las hojas de iOS */}\n <Drawer anchor=\"bottom\" open={isMoreOpen} onClose={closeMore}\n slotProps={{paper: {sx: {\n m: `${GLASS_PANEL_GAP_PIXELS}px auto calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n width: `min(${MORE_SHEET_MAX_WIDTH_PIXELS}px, calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px))`,\n maxHeight: MORE_SHEET_MAX_HEIGHT, borderRadius: '20px', overflow: 'hidden',\n }}}}>\n {isEditingTabs ? (\n <TabBarEditor menuItems={menuItems} pinnedKeys={tabs.map(menuItemKeyOf)}\n onDone={(keys) => {\n setPinnedKeys(keys);\n onPinnedTabsChange?.(keys);\n leaveEditor();\n }}\n onReset={() => {\n setPinnedKeys(null);\n onPinnedTabsChange?.(null);\n leaveEditor();\n }} />\n ) : (\n <>\n <Box sx={{display: 'flex', justifyContent: 'flex-end', px: 1.5, pt: 1, flexShrink: 0}}>\n <Button size=\"small\" autoFocus={returnsFromEditor} onClick={() => setIsEditingTabs(true)}>{t('drawer.editTabs')}</Button>\n </Box>\n <SideMenu menuItems={more} activeItem={activeItem} menuBadges={menuBadges} collapsed={false} showBrand={false} footer={moreFooter}\n onNavigate={closeMore} />\n </>\n )}\n </Drawer>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,7 @@ const PHONE_MEDIA_QUERY = "@media (max-width: 899.95px), (max-height: 499.95px)"
|
|
|
8
8
|
const DESKTOP_MEDIA_QUERY = "@media (min-width: 900px) and (min-height: 500px)";
|
|
9
9
|
const PHONE_LANDSCAPE_MEDIA_QUERY = "@media (max-height: 499.95px)";
|
|
10
10
|
const MOBILE_TAB_BAR_HEIGHT_PIXELS = Object.freeze({ portrait: 60, landscape: 46 });
|
|
11
|
+
const FLOATING_BOTTOM_SPACE_CSS_VARIABLE = "--calcita-floating-bottom-space";
|
|
11
12
|
function aboveMobileTabBarOf(gapPixels) {
|
|
12
13
|
return {
|
|
13
14
|
[PHONE_MEDIA_QUERY]: {
|
|
@@ -27,6 +28,7 @@ export {
|
|
|
27
28
|
DESKTOP_MEDIA_QUERY,
|
|
28
29
|
DRAWER_WIDTH_COLLAPSED,
|
|
29
30
|
DRAWER_WIDTH_EXPANDED,
|
|
31
|
+
FLOATING_BOTTOM_SPACE_CSS_VARIABLE,
|
|
30
32
|
GLASS_PANEL_GAP_PIXELS,
|
|
31
33
|
MAIN_CONTENT_ID,
|
|
32
34
|
MOBILE_TAB_BAR_HEIGHT_PIXELS,
|