@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InboxLayout.js","sources":["../../src/inbox/InboxLayout.jsx"],"sourcesContent":["// El acomodo de una bandeja tipo correo: las carpetas, la lista y la lectura. En escritorio, tres columnas; en el teléfono,\n// una a la vez: las carpetas como pestañas arriba de la lista y, al abrir un mensaje, la lectura con «Regresar».\n//\n// <InboxLayout folders={folders} selectedFolderId={folderId} onSelectFolder={setFolderId} toolbar={<Buscar y Redactar />}\n// list={<MessageList … />} detail={<MessageView … />} isDetailOpen={Boolean(selectedId)} onCloseDetail={() => setSelectedId(null)} />\n//\n// Escritorio o teléfono se decide con CSS (no con useMediaQuery): así el servidor pinta lo mismo que el navegador.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Badge from '@mui/material/Badge';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport Paper from '@mui/material/Paper';\nimport Tab from '@mui/material/Tab';\nimport Tabs from '@mui/material/Tabs';\nimport Typography from '@mui/material/Typography';\nimport ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';\n\nimport {DESKTOP_MEDIA_QUERY, PHONE_MEDIA_QUERY} from '@/appShell/shellSizes.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\nconst FOLDERS_WIDTH_PIXELS = 220;\nconst LIST_WIDTH_PIXELS = 380;\n// la bandeja ocupa el alto de la ventana (menos la barra y el encabezado de la página): cada columna desliza por dentro\nconst DESKTOP_HEIGHT = 'calc(100dvh - 220px)';\nconst DESKTOP_MIN_HEIGHT_PIXELS = 480;\n\n/**\n * @param {object} props\n * @param {Array<{id: string, label: string, Icon?: import('react').ElementType, unreadCount?: number}>} props.folders\n * @param {string} props.selectedFolderId\n * @param {(folderId: string) => void} props.onSelectFolder\n * @param {import('react').ReactNode} [props.toolbar] arriba de la lista (buscar, redactar)\n * @param {import('react').ReactNode} props.list normalmente MessageList\n * @param {import('react').ReactNode} props.detail normalmente MessageView\n * @param {boolean} [props.isDetailOpen] en el teléfono, se ve la lectura en lugar de la lista\n * @param {() => void} [props.onCloseDetail] «Regresar» del teléfono\n */\nexport default function InboxLayout({folders, selectedFolderId, onSelectFolder, toolbar, list, detail, isDetailOpen = false, onCloseDetail}) {\n const {t} = useToolkitTranslation('inbox');\n\n return (\n <Box sx={{\n display: 'grid', gap: 2,\n [DESKTOP_MEDIA_QUERY]: {\n gridTemplateColumns: `${FOLDERS_WIDTH_PIXELS}px ${LIST_WIDTH_PIXELS}px minmax(0, 1fr)`, height: DESKTOP_HEIGHT, minHeight: DESKTOP_MIN_HEIGHT_PIXELS,\n },\n }}>\n {/* las carpetas: columna en escritorio */}\n <Paper variant=\"outlined\" component=\"nav\" aria-label={t('folders')} sx={{p: 1, overflowY: 'auto', borderRadius: 3, [PHONE_MEDIA_QUERY]: {display: 'none'}}}>\n <List disablePadding>\n {folders.map(({id, label, Icon, unreadCount}) => (\n <ListItemButton key={id} selected={id === selectedFolderId} onClick={() => onSelectFolder(id)} sx={{borderRadius: 2}}>\n {Icon && <ListItemIcon sx={{minWidth: 36}}><Icon fontSize=\"small\" /></ListItemIcon>}\n <ListItemText primary={label} />\n {unreadCount > 0 && <Typography variant=\"caption\" sx={{fontWeight: 700}} aria-label={t('unreadCount', {count: unreadCount})}>{unreadCount}</Typography>}\n </ListItemButton>\n ))}\n </List>\n </Paper>\n\n {/* la lista, con las carpetas como pestañas en el teléfono */}\n <Paper variant=\"outlined\" sx={{display: 'flex', flexDirection: 'column', minHeight: 0, borderRadius: 3, overflow: 'hidden',\n [PHONE_MEDIA_QUERY]: isDetailOpen ? {display: 'none'} : {}}}>\n <Tabs value={selectedFolderId} onChange={(event, folderId) => onSelectFolder(folderId)} variant=\"scrollable\" aria-label={t('folders')}\n sx={{px: 1, [DESKTOP_MEDIA_QUERY]: {display: 'none'}}}>\n {folders.map(({id, label, unreadCount}) => (\n <Tab key={id} value={id} label={unreadCount > 0 ? <Badge color=\"primary\" badgeContent={unreadCount} sx={{pr: 1.5}}>{label}</Badge> : label} />\n ))}\n </Tabs>\n {toolbar && <Box sx={{p: 1.5, pb: 1}}>{toolbar}</Box>}\n <Box sx={{flex: 1, minHeight: 0, overflowY: 'auto', px: 1, pb: 1}}>{list}</Box>\n </Paper>\n\n {/* la lectura: en el teléfono solo con un mensaje abierto, con su «Regresar» */}\n <Paper variant=\"outlined\" sx={{p: {xs: 2, md: 3}, overflowY: 'auto', borderRadius: 3, [PHONE_MEDIA_QUERY]: isDetailOpen ? {} : {display: 'none'}}}>\n {onCloseDetail && (\n <Button startIcon={<ArrowBackRoundedIcon />} onClick={onCloseDetail} sx={{mb: 1, [DESKTOP_MEDIA_QUERY]: {display: 'none'}}}>{t('back')}</Button>\n )}\n {detail}\n </Paper>\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InboxLayout.js","sources":["../../src/inbox/InboxLayout.jsx"],"sourcesContent":["// El acomodo de una bandeja tipo correo: las carpetas, la lista y la lectura. En escritorio, tres columnas; en el teléfono,\n// una a la vez: las carpetas como pestañas arriba de la lista y, al abrir un mensaje, la lectura con «Regresar».\n//\n// <InboxLayout folders={folders} selectedFolderId={folderId} onSelectFolder={setFolderId} toolbar={<Buscar y Redactar />}\n// list={<MessageList … />} detail={<MessageView … />} isDetailOpen={Boolean(selectedId)} onCloseDetail={() => setSelectedId(null)} />\n//\n// Debajo de las carpetas, las etiquetas del proyecto (labels) con su punto de color, como Gmail; se eligen igual que una\n// carpeta. foldersHeader pone algo arriba de las carpetas (un botón «Redactar») y texts cambia cualquier texto.\n//\n// Escritorio o teléfono se decide con CSS (no con useMediaQuery): así el servidor pinta lo mismo que el navegador.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Badge from '@mui/material/Badge';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport ListSubheader from '@mui/material/ListSubheader';\nimport Paper from '@mui/material/Paper';\nimport Tab from '@mui/material/Tab';\nimport Tabs from '@mui/material/Tabs';\nimport Typography from '@mui/material/Typography';\nimport ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';\n\nimport {DESKTOP_MEDIA_QUERY, PHONE_MEDIA_QUERY} from '@/appShell/shellSizes.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\nconst FOLDERS_WIDTH_PIXELS = 220;\nconst LIST_WIDTH_PIXELS = 380;\n// la bandeja ocupa el alto de la ventana (menos la barra y el encabezado de la página): cada columna desliza por dentro\nconst DESKTOP_HEIGHT = 'calc(100dvh - 220px)';\nconst DESKTOP_MIN_HEIGHT_PIXELS = 480;\nconst LABEL_DOT_PIXELS = 10;\n\n/** Un renglón de carpeta o etiqueta: su ícono (o su punto de color), su nombre y cuántos sin leer. */\nfunction FolderRow({folder, isSelected, onSelect, t}) {\n const {id, label, Icon, color, unreadCount} = folder;\n let icon = null;\n if (Icon) {\n icon = <Icon fontSize=\"small\" />;\n } else if (color) {\n icon = <Box aria-hidden=\"true\" sx={{width: LABEL_DOT_PIXELS, height: LABEL_DOT_PIXELS, borderRadius: '50%', bgcolor: `${color}.main`, ml: 0.5}} />;\n }\n return (\n <ListItemButton selected={isSelected} onClick={() => onSelect(id)} sx={{borderRadius: 2}}>\n {icon && <ListItemIcon sx={{minWidth: 36}}>{icon}</ListItemIcon>}\n <ListItemText primary={label} />\n {unreadCount > 0 && <Typography variant=\"caption\" sx={{fontWeight: 700}} aria-label={t('unreadCount', {count: unreadCount})}>{unreadCount}</Typography>}\n </ListItemButton>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, label: string, Icon?: import('react').ElementType, unreadCount?: number}>} props.folders\n * @param {Array<{id: string, label: string, color?: string, unreadCount?: number}>} [props.labels] las etiquetas, debajo de las\n * carpetas con su punto de color (un color del tema); su id no debe repetir el de una carpeta\n * @param {string} props.selectedFolderId la carpeta o etiqueta elegida\n * @param {(folderId: string) => void} props.onSelectFolder\n * @param {import('react').ReactNode} [props.toolbar] arriba de la lista (buscar, redactar)\n * @param {import('react').ReactNode} props.list normalmente MessageList\n * @param {import('react').ReactNode} props.detail normalmente MessageView\n * @param {boolean} [props.isDetailOpen] en el teléfono, se ve la lectura en lugar de la lista\n * @param {() => void} [props.onCloseDetail] «Regresar» del teléfono\n * @param {import('react').ReactNode} [props.foldersHeader] arriba de las carpetas en escritorio (un botón «Redactar»)\n * @param {object} [props.texts] los textos que cambian, con la forma de `inbox` en los textos del paquete\n */\nexport default function InboxLayout({\n folders, labels = [], selectedFolderId, onSelectFolder, toolbar, list, detail, isDetailOpen = false, onCloseDetail, foldersHeader, texts,\n}) {\n const {t} = useToolkitTranslation('inbox', texts);\n\n return (\n <Box sx={{\n display: 'grid', gap: 2,\n [DESKTOP_MEDIA_QUERY]: {\n gridTemplateColumns: `${FOLDERS_WIDTH_PIXELS}px ${LIST_WIDTH_PIXELS}px minmax(0, 1fr)`, height: DESKTOP_HEIGHT, minHeight: DESKTOP_MIN_HEIGHT_PIXELS,\n },\n }}>\n {/* las carpetas: columna en escritorio */}\n <Paper variant=\"outlined\" component=\"nav\" aria-label={t('folders')} sx={{p: 1, overflowY: 'auto', borderRadius: 3, [PHONE_MEDIA_QUERY]: {display: 'none'}}}>\n {foldersHeader && <Box sx={{p: 0.5, pb: 1}}>{foldersHeader}</Box>}\n <List disablePadding>\n {folders.map((folder) => (\n <FolderRow key={folder.id} folder={folder} isSelected={folder.id === selectedFolderId} onSelect={onSelectFolder} t={t} />\n ))}\n </List>\n {labels.length > 0 && (\n <List disablePadding aria-label={t('labels')}\n subheader={<ListSubheader disableSticky sx={{bgcolor: 'transparent', lineHeight: 2.5, mt: 1}}>{t('labels')}</ListSubheader>}>\n {labels.map((label) => (\n <FolderRow key={label.id} folder={label} isSelected={label.id === selectedFolderId} onSelect={onSelectFolder} t={t} />\n ))}\n </List>\n )}\n </Paper>\n\n {/* la lista, con las carpetas como pestañas en el teléfono */}\n <Paper variant=\"outlined\" sx={{display: 'flex', flexDirection: 'column', minHeight: 0, borderRadius: 3, overflow: 'hidden',\n [PHONE_MEDIA_QUERY]: isDetailOpen ? {display: 'none'} : {}}}>\n <Tabs value={selectedFolderId} onChange={(event, folderId) => onSelectFolder(folderId)} variant=\"scrollable\" aria-label={t('folders')}\n sx={{px: 1, [DESKTOP_MEDIA_QUERY]: {display: 'none'}}}>\n {[...folders, ...labels].map(({id, label, unreadCount}) => (\n <Tab key={id} value={id} label={unreadCount > 0 ? <Badge color=\"primary\" badgeContent={unreadCount} sx={{pr: 1.5}}>{label}</Badge> : label} />\n ))}\n </Tabs>\n {toolbar && <Box sx={{p: 1.5, pb: 1}}>{toolbar}</Box>}\n <Box sx={{flex: 1, minHeight: 0, overflowY: 'auto', px: 1, pb: 1}}>{list}</Box>\n </Paper>\n\n {/* la lectura: en el teléfono solo con un mensaje abierto, con su «Regresar» */}\n <Paper variant=\"outlined\" sx={{p: {xs: 2, md: 3}, overflowY: 'auto', borderRadius: 3, [PHONE_MEDIA_QUERY]: isDetailOpen ? {} : {display: 'none'}}}>\n {onCloseDetail && (\n <Button startIcon={<ArrowBackRoundedIcon />} onClick={onCloseDetail} sx={{mb: 1, [DESKTOP_MEDIA_QUERY]: {display: 'none'}}}>{t('back')}</Button>\n )}\n {detail}\n </Paper>\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import Avatar from "@mui/material/Avatar";
|
|
4
4
|
import Box from "@mui/material/Box";
|
|
5
|
+
import ButtonBase from "@mui/material/ButtonBase";
|
|
6
|
+
import Chip from "@mui/material/Chip";
|
|
5
7
|
import IconButton from "@mui/material/IconButton";
|
|
6
8
|
import List from "@mui/material/List";
|
|
7
9
|
import ListItem from "@mui/material/ListItem";
|
|
@@ -10,48 +12,96 @@ import ListItemButton from "@mui/material/ListItemButton";
|
|
|
10
12
|
import Stack from "@mui/material/Stack";
|
|
11
13
|
import Typography from "@mui/material/Typography";
|
|
12
14
|
import AttachFileRoundedIcon from "@mui/icons-material/AttachFileRounded";
|
|
15
|
+
import CheckIcon from "@mui/icons-material/CheckRounded";
|
|
13
16
|
import StarBorderRoundedIcon from "@mui/icons-material/StarBorderRounded";
|
|
14
17
|
import StarRoundedIcon from "@mui/icons-material/StarRounded";
|
|
15
18
|
import { initialsOf } from "../appShell/menuItemText.js";
|
|
16
19
|
import { inboxDateOf } from "./inboxDates.js";
|
|
20
|
+
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
21
|
+
import { tagOf } from "../ui/labels/tagOf.js";
|
|
17
22
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
23
|
+
import ActionsMenu from "../ui/actions/ActionsMenu.js";
|
|
18
24
|
const UNREAD_DOT_PIXELS = 8;
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
const AVATAR_PIXELS = 40;
|
|
26
|
+
function MessageList({
|
|
27
|
+
messages,
|
|
28
|
+
selectedId = null,
|
|
29
|
+
onSelect,
|
|
30
|
+
onToggleStar,
|
|
31
|
+
selectedIds = [],
|
|
32
|
+
onSelectionChange,
|
|
33
|
+
messageActions,
|
|
34
|
+
renderMessage,
|
|
35
|
+
emptyText,
|
|
36
|
+
texts
|
|
37
|
+
}) {
|
|
38
|
+
const { t } = useToolkitTranslation("inbox", texts);
|
|
39
|
+
const formatters = useFormatters();
|
|
21
40
|
if (messages.length === 0) {
|
|
22
41
|
return /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { p: 3, textAlign: "center" }, children: emptyText ?? t("empty") });
|
|
23
42
|
}
|
|
43
|
+
const toggleSelected = (messageId, isChecked) => {
|
|
44
|
+
onSelectionChange(isChecked ? [...selectedIds, messageId] : selectedIds.filter((id) => id !== messageId));
|
|
45
|
+
};
|
|
24
46
|
return /* @__PURE__ */ jsx(List, { disablePadding: true, "aria-label": t("listLabel"), children: messages.map((message) => {
|
|
25
47
|
const weight = message.isUnread ? 700 : 400;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
const isChecked = selectedIds.includes(message.id);
|
|
49
|
+
const avatar = /* @__PURE__ */ jsx(Avatar, { sx: { width: AVATAR_PIXELS, height: AVATAR_PIXELS }, children: initialsOf(message.from.name) });
|
|
50
|
+
return /* @__PURE__ */ jsx(ListItem, { disablePadding: true, secondaryAction: (onToggleStar || messageActions) && /* @__PURE__ */ jsxs(Stack, { direction: "row", sx: { alignItems: "center" }, children: [
|
|
51
|
+
onToggleStar && /* @__PURE__ */ jsx(
|
|
52
|
+
IconButton,
|
|
53
|
+
{
|
|
54
|
+
size: "small",
|
|
55
|
+
onClick: () => onToggleStar(message.id),
|
|
56
|
+
"aria-pressed": Boolean(message.isStarred),
|
|
57
|
+
"aria-label": message.isStarred ? t("unstar") : t("star"),
|
|
58
|
+
sx: { color: message.isStarred ? "warning.main" : "text.secondary" },
|
|
59
|
+
children: message.isStarred ? /* @__PURE__ */ jsx(StarRoundedIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(StarBorderRoundedIcon, { fontSize: "small" })
|
|
60
|
+
}
|
|
61
|
+
),
|
|
62
|
+
messageActions && /* @__PURE__ */ jsx(ActionsMenu, { label: t("messageActions", { subject: message.subject }), actions: messageActions(message) })
|
|
63
|
+
] }), sx: { "& .MuiListItemSecondaryAction-root": { right: 4 } }, children: /* @__PURE__ */ jsxs(
|
|
38
64
|
ListItemButton,
|
|
39
65
|
{
|
|
40
|
-
selected: message.id === selectedId,
|
|
66
|
+
selected: message.id === selectedId || isChecked,
|
|
41
67
|
onClick: () => onSelect(message.id),
|
|
42
68
|
alignItems: "flex-start",
|
|
43
|
-
sx: { borderRadius: 2, pr: 6 },
|
|
69
|
+
sx: { borderRadius: 2, pr: onToggleStar && messageActions ? 10 : 6 },
|
|
44
70
|
children: [
|
|
45
|
-
/* @__PURE__ */ jsx(ListItemAvatar, { sx: { mt: 0.5 }, children: /* @__PURE__ */ jsx(
|
|
46
|
-
|
|
71
|
+
/* @__PURE__ */ jsx(ListItemAvatar, { sx: { mt: 0.5 }, children: onSelectionChange ? /* @__PURE__ */ jsx(
|
|
72
|
+
ButtonBase,
|
|
73
|
+
{
|
|
74
|
+
role: "checkbox",
|
|
75
|
+
"aria-checked": isChecked,
|
|
76
|
+
"aria-label": t("select", { subject: message.subject }),
|
|
77
|
+
sx: { borderRadius: "50%" },
|
|
78
|
+
onClick: (event) => {
|
|
79
|
+
event.stopPropagation();
|
|
80
|
+
toggleSelected(message.id, !isChecked);
|
|
81
|
+
},
|
|
82
|
+
children: isChecked ? /* @__PURE__ */ jsx(Avatar, { sx: { width: AVATAR_PIXELS, height: AVATAR_PIXELS, bgcolor: "primary.main", color: "primary.contrastText" }, children: /* @__PURE__ */ jsx(CheckIcon, {}) }) : avatar
|
|
83
|
+
}
|
|
84
|
+
) : avatar }),
|
|
85
|
+
renderMessage ? /* @__PURE__ */ jsx(Box, { sx: { minWidth: 0, flex: 1 }, children: renderMessage(message) }) : /* @__PURE__ */ jsxs(Box, { sx: { minWidth: 0, flex: 1 }, children: [
|
|
47
86
|
/* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, sx: { alignItems: "center" }, children: [
|
|
48
87
|
message.isUnread && /* @__PURE__ */ jsx(Box, { component: "span", "aria-label": t("unread"), sx: { width: UNREAD_DOT_PIXELS, height: UNREAD_DOT_PIXELS, borderRadius: "50%", bgcolor: "primary.main", flexShrink: 0 } }),
|
|
49
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", noWrap: true, sx: { fontWeight: weight, flex: 1 }, children: [
|
|
89
|
+
message.from.name,
|
|
90
|
+
message.threadCount > 1 && /* @__PURE__ */ jsxs(Typography, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 0.5 }, "aria-label": t("threadCount", { count: message.threadCount }), children: [
|
|
91
|
+
"(",
|
|
92
|
+
message.threadCount,
|
|
93
|
+
")"
|
|
94
|
+
] })
|
|
95
|
+
] }),
|
|
50
96
|
message.hasAttachments && /* @__PURE__ */ jsx(AttachFileRoundedIcon, { fontSize: "inherit", color: "action", "aria-label": t("attachments") }),
|
|
51
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", sx: { flexShrink: 0, fontVariantNumeric: "tabular-nums" }, children: inboxDateOf(message.receivedAt) })
|
|
97
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", sx: { flexShrink: 0, fontVariantNumeric: "tabular-nums" }, children: inboxDateOf(message.receivedAt, /* @__PURE__ */ new Date(), formatters) })
|
|
52
98
|
] }),
|
|
53
99
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", noWrap: true, sx: { fontWeight: weight }, children: message.subject }),
|
|
54
|
-
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", noWrap: true, children: message.preview })
|
|
100
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", noWrap: true, children: message.preview }),
|
|
101
|
+
message.labels?.length > 0 && /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap", mt: 0.5 }, children: message.labels.map((rawLabel) => {
|
|
102
|
+
const label = tagOf(rawLabel);
|
|
103
|
+
return /* @__PURE__ */ jsx(Chip, { size: "small", variant: "outlined", color: label.color, label: label.label }, label.label);
|
|
104
|
+
}) })
|
|
55
105
|
] })
|
|
56
106
|
]
|
|
57
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.js","sources":["../../src/inbox/MessageList.jsx"],"sourcesContent":["// La lista de mensajes de una bandeja tipo correo: quién lo manda, el asunto y el principio del texto, la fecha corta, la\n// estrella y el clip si trae archivos. Los no leídos van en negritas con su punto.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Avatar from '@mui/material/Avatar';\nimport Box from '@mui/material/Box';\nimport IconButton from '@mui/material/IconButton';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemAvatar from '@mui/material/ListItemAvatar';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\nimport StarBorderRoundedIcon from '@mui/icons-material/StarBorderRounded';\nimport StarRoundedIcon from '@mui/icons-material/StarRounded';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {inboxDateOf} from '@/inbox/inboxDates.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\nconst UNREAD_DOT_PIXELS = 8;\n\n/**\n * @param {object} props\n * @param {Array<{id: string, from: {name: string, email: string}, subject: string, preview: string, receivedAt: string, isUnread?: boolean, isStarred?: boolean, hasAttachments?: boolean}>} props.messages\n * @param {string|null} [props.selectedId]\n * @param {(messageId: string) => void} props.onSelect\n * @param {(messageId: string) => void} [props.onToggleStar]\n * @param {string} [props.emptyText] ya traducido; por omisión el del paquete\n */\nexport default function MessageList({messages, selectedId = null, onSelect, onToggleStar, emptyText}) {\n const {t} = useToolkitTranslation('inbox');\n\n if (messages.length === 0) {\n return <Typography variant=\"body2\" color=\"text.secondary\" sx={{p: 3, textAlign: 'center'}}>{emptyText ?? t('empty')}</Typography>;\n }\n\n return (\n <List disablePadding aria-label={t('listLabel')}>\n {messages.map((message) => {\n const weight = message.isUnread ? 700 : 400;\n return (\n <ListItem key={message.id} disablePadding secondaryAction={onToggleStar && (\n <IconButton edge=\"end\" size=\"small\" onClick={() => onToggleStar(message.id)} aria-pressed={Boolean(message.isStarred)}\n aria-label={message.isStarred ? t('unstar') : t('star')} sx={{color: message.isStarred ? 'warning.main' : 'text.secondary'}}>\n {message.isStarred ? <StarRoundedIcon fontSize=\"small\" /> : <StarBorderRoundedIcon fontSize=\"small\" />}\n </IconButton>\n )}>\n <ListItemButton selected={message.id === selectedId} onClick={() => onSelect(message.id)} alignItems=\"flex-start\"\n sx={{borderRadius: 2, pr: 6}}>\n <ListItemAvatar sx={{mt: 0.5}}><Avatar>{initialsOf(message.from.name)}</Avatar></ListItemAvatar>\n <Box sx={{minWidth: 0, flex: 1}}>\n <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center'}}>\n {message.isUnread && (\n <Box component=\"span\" aria-label={t('unread')} sx={{width: UNREAD_DOT_PIXELS, height: UNREAD_DOT_PIXELS, borderRadius: '50%', bgcolor: 'primary.main', flexShrink: 0}} />\n )}\n <Typography variant=\"body2\" noWrap sx={{fontWeight: weight, flex: 1}}>{message.from.name}</Typography>\n {message.hasAttachments && <AttachFileRoundedIcon fontSize=\"inherit\" color=\"action\" aria-label={t('attachments')} />}\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{flexShrink: 0, fontVariantNumeric: 'tabular-nums'}}>\n {inboxDateOf(message.receivedAt)}\n </Typography>\n </Stack>\n <Typography variant=\"body2\" noWrap sx={{fontWeight: weight}}>{message.subject}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{message.preview}</Typography>\n </Box>\n </ListItemButton>\n </ListItem>\n );\n })}\n </List>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAqBA,MAAM,oBAAoB;AAU1B,SAAwB,YAAY,EAAC,UAAU,aAAa,MAAM,UAAU,cAAc,aAAY;AAClG,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AAEzC,MAAI,SAAS,WAAW,GAAG;AACvB,+BAAQ,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,IAAI,EAAC,GAAG,GAAG,WAAW,YAAY,UAAA,aAAa,EAAE,OAAO,GAAE;AAAA,EACxH;AAEA,SACI,oBAAC,MAAA,EAAK,gBAAc,MAAC,cAAY,EAAE,WAAW,GACzC,UAAA,SAAS,IAAI,CAAC,YAAY;AACvB,UAAM,SAAS,QAAQ,WAAW,MAAM;AACxC,WACI,oBAAC,UAAA,EAA0B,gBAAc,MAAC,iBAAiB,gBACvD;AAAA,MAAC;AAAA,MAAA;AAAA,QAAW,MAAK;AAAA,QAAM,MAAK;AAAA,QAAQ,SAAS,MAAM,aAAa,QAAQ,EAAE;AAAA,QAAG,gBAAc,QAAQ,QAAQ,SAAS;AAAA,QACxG,cAAY,QAAQ,YAAY,EAAE,QAAQ,IAAI,EAAE,MAAM;AAAA,QAAG,IAAI,EAAC,OAAO,QAAQ,YAAY,iBAAiB,iBAAA;AAAA,QACjH,UAAA,QAAQ,YAAY,oBAAC,iBAAA,EAAgB,UAAS,SAAQ,IAAK,oBAAC,uBAAA,EAAsB,UAAS,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA,GAGxG,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAe,UAAU,QAAQ,OAAO;AAAA,QAAY,SAAS,MAAM,SAAS,QAAQ,EAAE;AAAA,QAAG,YAAW;AAAA,QACrF,IAAI,EAAC,cAAc,GAAG,IAAI,EAAA;AAAA,QACtC,UAAA;AAAA,UAAA,oBAAC,gBAAA,EAAe,IAAI,EAAC,IAAI,IAAA,GAAM,UAAA,oBAAC,QAAA,EAAQ,UAAA,WAAW,QAAQ,KAAK,IAAI,GAAE,GAAS;AAAA,UAC/E,qBAAC,OAAI,IAAI,EAAC,UAAU,GAAG,MAAM,KACzB,UAAA;AAAA,YAAA,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,IAAI,EAAC,YAAY,SAAA,GAC/C,UAAA;AAAA,cAAA,QAAQ,gCACJ,KAAA,EAAI,WAAU,QAAO,cAAY,EAAE,QAAQ,GAAG,IAAI,EAAC,OAAO,mBAAmB,QAAQ,mBAAmB,cAAc,OAAO,SAAS,gBAAgB,YAAY,EAAA,GAAI;AAAA,cAE3K,oBAAC,YAAA,EAAW,SAAQ,SAAQ,QAAM,MAAC,IAAI,EAAC,YAAY,QAAQ,MAAM,EAAA,GAAK,UAAA,QAAQ,KAAK,MAAK;AAAA,cACxF,QAAQ,kBAAkB,oBAAC,uBAAA,EAAsB,UAAS,WAAU,OAAM,UAAS,cAAY,EAAE,aAAa,EAAA,CAAG;AAAA,kCACjH,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,IAAI,EAAC,YAAY,GAAG,oBAAoB,eAAA,GACxF,UAAA,YAAY,QAAQ,UAAU,EAAA,CACnC;AAAA,YAAA,GACJ;AAAA,YACA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,QAAM,MAAC,IAAI,EAAC,YAAY,OAAA,GAAU,UAAA,QAAQ,QAAA,CAAQ;AAAA,YAC9E,oBAAC,cAAW,SAAQ,SAAQ,OAAM,kBAAiB,QAAM,MAAE,UAAA,QAAQ,QAAA,CAAQ;AAAA,UAAA,EAAA,CAC/E;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,EACJ,GAvBW,QAAQ,EAwBvB;AAAA,EAER,CAAC,EAAA,CACL;AAER;"}
|
|
1
|
+
{"version":3,"file":"MessageList.js","sources":["../../src/inbox/MessageList.jsx"],"sourcesContent":["// La lista de mensajes de una bandeja tipo correo: quién lo manda (y cuántos mensajes trae la conversación), el asunto con sus\n// etiquetas, el principio del texto, la fecha corta, la estrella y el clip si trae archivos. Los no leídos van en negritas con\n// su punto.\n//\n// Lo de un producto, todo hacia afuera (la lista no guarda nada ni llama a ningún servidor):\n// - elegir varios: con onSelectionChange el monograma de cada mensaje se vuelve su casilla (como Gmail en el teléfono) y la\n// pantalla pone las acciones de lo elegido (SelectionActions: archivar, borrar, marcar como leído…);\n// - «⋯» de cada mensaje con las acciones que dé la pantalla (messageActions);\n// - renderMessage para pintar lo de adentro de cada renglón a su manera, y texts para cualquier texto.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Avatar from '@mui/material/Avatar';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport IconButton from '@mui/material/IconButton';\nimport List from '@mui/material/List';\nimport ListItem from '@mui/material/ListItem';\nimport ListItemAvatar from '@mui/material/ListItemAvatar';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\nimport CheckRoundedIcon from '@mui/icons-material/CheckRounded';\nimport StarBorderRoundedIcon from '@mui/icons-material/StarBorderRounded';\nimport StarRoundedIcon from '@mui/icons-material/StarRounded';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {inboxDateOf} from '@/inbox/inboxDates.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {tagOf} from '@/ui/labels/tagOf.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ActionsMenu from '@/ui/actions/ActionsMenu.jsx';\n\nconst UNREAD_DOT_PIXELS = 8;\nconst AVATAR_PIXELS = 40;\n\n/**\n * @param {object} props\n * @param {Array<{id: string, from: {name: string, email: string}, subject: string, preview: string, receivedAt: string, isUnread?: boolean, isStarred?: boolean, hasAttachments?: boolean, threadCount?: number, labels?: Array<string|{label: string, color?: string}>} & Record<string, any>>} props.messages\n * cada mensaje puede traer sus campos propios (llegan a renderMessage y a messageActions tal cual). threadCount: cuántos\n * mensajes trae la conversación (con 2 o más se pinta «(3)»); labels: texto o {label, color} del tema\n * @param {string|null} [props.selectedId] el mensaje abierto\n * @param {(messageId: string) => void} props.onSelect abrir un mensaje\n * @param {(messageId: string) => void} [props.onToggleStar]\n * @param {string[]} [props.selectedIds] los elegidos para una acción en grupo\n * @param {(messageIds: string[]) => void} [props.onSelectionChange] con él, cada mensaje se puede elegir picando su monograma\n * @param {(message: any) => Array<{key: string, label: string, icon?: import('react').ReactNode, color?: string, disabled?: boolean, onClick: () => void}>} [props.messageActions]\n * las acciones del «⋯» de cada mensaje (archivar, posponer, mover…); sin ellas no hay «⋯»\n * @param {(message: any) => import('react').ReactNode} [props.renderMessage] lo de adentro de cada renglón, a su manera\n * @param {string} [props.emptyText] ya traducido; por omisión el del paquete\n * @param {object} [props.texts] los textos que cambian, con la forma de `inbox` en los textos del paquete\n */\nexport default function MessageList({\n messages, selectedId = null, onSelect, onToggleStar, selectedIds = [], onSelectionChange, messageActions, renderMessage, emptyText, texts,\n}) {\n const {t} = useToolkitTranslation('inbox', texts);\n const formatters = useFormatters();\n\n if (messages.length === 0) {\n return <Typography variant=\"body2\" color=\"text.secondary\" sx={{p: 3, textAlign: 'center'}}>{emptyText ?? t('empty')}</Typography>;\n }\n\n const toggleSelected = (messageId, isChecked) => {\n onSelectionChange(isChecked ? [...selectedIds, messageId] : selectedIds.filter((id) => id !== messageId));\n };\n\n return (\n <List disablePadding aria-label={t('listLabel')}>\n {messages.map((message) => {\n const weight = message.isUnread ? 700 : 400;\n const isChecked = selectedIds.includes(message.id);\n const avatar = <Avatar sx={{width: AVATAR_PIXELS, height: AVATAR_PIXELS}}>{initialsOf(message.from.name)}</Avatar>;\n return (\n <ListItem key={message.id} disablePadding secondaryAction={(onToggleStar || messageActions) && (\n <Stack direction=\"row\" sx={{alignItems: 'center'}}>\n {onToggleStar && (\n <IconButton size=\"small\" onClick={() => onToggleStar(message.id)} aria-pressed={Boolean(message.isStarred)}\n aria-label={message.isStarred ? t('unstar') : t('star')} sx={{color: message.isStarred ? 'warning.main' : 'text.secondary'}}>\n {message.isStarred ? <StarRoundedIcon fontSize=\"small\" /> : <StarBorderRoundedIcon fontSize=\"small\" />}\n </IconButton>\n )}\n {messageActions && <ActionsMenu label={t('messageActions', {subject: message.subject})} actions={messageActions(message)} />}\n </Stack>\n )} sx={{'& .MuiListItemSecondaryAction-root': {right: 4}}}>\n <ListItemButton selected={message.id === selectedId || isChecked} onClick={() => onSelect(message.id)} alignItems=\"flex-start\"\n sx={{borderRadius: 2, pr: onToggleStar && messageActions ? 10 : 6}}>\n <ListItemAvatar sx={{mt: 0.5}}>\n {/* elegir: el monograma es la casilla (el clic no abre el mensaje). Un botón con papel de casilla y\n no el Checkbox de MUI: el tema le pinta a la casilla marcada un cuadro blanco detrás de su dibujo\n y la palomita salía como un cuadro */}\n {onSelectionChange ? (\n <ButtonBase role=\"checkbox\" aria-checked={isChecked} aria-label={t('select', {subject: message.subject})}\n sx={{borderRadius: '50%'}}\n onClick={(event) => {\n event.stopPropagation();\n toggleSelected(message.id, !isChecked);\n }}>\n {isChecked ? (\n <Avatar sx={{width: AVATAR_PIXELS, height: AVATAR_PIXELS, bgcolor: 'primary.main', color: 'primary.contrastText'}}>\n <CheckRoundedIcon />\n </Avatar>\n ) : avatar}\n </ButtonBase>\n ) : avatar}\n </ListItemAvatar>\n {renderMessage ? <Box sx={{minWidth: 0, flex: 1}}>{renderMessage(message)}</Box> : (\n <Box sx={{minWidth: 0, flex: 1}}>\n <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center'}}>\n {message.isUnread && (\n <Box component=\"span\" aria-label={t('unread')} sx={{width: UNREAD_DOT_PIXELS, height: UNREAD_DOT_PIXELS, borderRadius: '50%', bgcolor: 'primary.main', flexShrink: 0}} />\n )}\n <Typography variant=\"body2\" noWrap sx={{fontWeight: weight, flex: 1}}>\n {message.from.name}\n {message.threadCount > 1 && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\" sx={{ml: 0.5}} aria-label={t('threadCount', {count: message.threadCount})}>\n ({message.threadCount})\n </Typography>\n )}\n </Typography>\n {message.hasAttachments && <AttachFileRoundedIcon fontSize=\"inherit\" color=\"action\" aria-label={t('attachments')} />}\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{flexShrink: 0, fontVariantNumeric: 'tabular-nums'}}>\n {inboxDateOf(message.receivedAt, new Date(), formatters)}\n </Typography>\n </Stack>\n <Typography variant=\"body2\" noWrap sx={{fontWeight: weight}}>{message.subject}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{message.preview}</Typography>\n {message.labels?.length > 0 && (\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{flexWrap: 'wrap', mt: 0.5}}>\n {message.labels.map((rawLabel) => {\n const label = tagOf(rawLabel);\n return <Chip key={label.label} size=\"small\" variant=\"outlined\" color={label.color} label={label.label} />;\n })}\n </Stack>\n )}\n </Box>\n )}\n </ListItemButton>\n </ListItem>\n );\n })}\n </List>\n );\n}\n"],"names":["CheckRoundedIcon"],"mappings":"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"}
|
|
@@ -1,50 +1,101 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
3
4
|
import Avatar from "@mui/material/Avatar";
|
|
4
5
|
import Box from "@mui/material/Box";
|
|
6
|
+
import ButtonBase from "@mui/material/ButtonBase";
|
|
5
7
|
import Chip from "@mui/material/Chip";
|
|
6
8
|
import Divider from "@mui/material/Divider";
|
|
7
9
|
import Stack from "@mui/material/Stack";
|
|
8
10
|
import Typography from "@mui/material/Typography";
|
|
9
11
|
import AttachFileRoundedIcon from "@mui/icons-material/AttachFileRounded";
|
|
10
12
|
import { initialsOf } from "../appShell/menuItemText.js";
|
|
11
|
-
import {
|
|
13
|
+
import { useFormatters } from "../toolkit/useFormatters.js";
|
|
14
|
+
import { inboxDateOf } from "./inboxDates.js";
|
|
12
15
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
16
|
+
import { tagOf } from "../ui/labels/tagOf.js";
|
|
17
|
+
const PREVIEW_LENGTH = 90;
|
|
18
|
+
function MessageHeader({ entry, t }) {
|
|
19
|
+
const { formatDateTime } = useFormatters();
|
|
20
|
+
return /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1.5, sx: { alignItems: "center" }, children: [
|
|
21
|
+
/* @__PURE__ */ jsx(Avatar, { children: initialsOf(entry.from.name) }),
|
|
22
|
+
/* @__PURE__ */ jsxs(Box, { sx: { minWidth: 0, flex: 1 }, children: [
|
|
23
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "subtitle2", component: "p", noWrap: true, children: [
|
|
24
|
+
entry.from.name,
|
|
25
|
+
" ",
|
|
26
|
+
/* @__PURE__ */ jsxs(Typography, { component: "span", variant: "body2", color: "text.secondary", children: [
|
|
27
|
+
"<",
|
|
28
|
+
entry.from.email,
|
|
29
|
+
">"
|
|
30
|
+
] })
|
|
31
|
+
] }),
|
|
32
|
+
entry.to?.length > 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", component: "p", children: t("to", { to: entry.to.join(", ") }) }),
|
|
33
|
+
entry.cc?.length > 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", component: "p", children: t("cc", { cc: entry.cc.join(", ") }) }),
|
|
34
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", component: "p", children: formatDateTime(entry.receivedAt) })
|
|
35
|
+
] })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
function MessageBody({ entry, t, onAttachmentClick, renderBody }) {
|
|
39
|
+
return /* @__PURE__ */ jsxs(Stack, { spacing: 2, children: [
|
|
40
|
+
/* @__PURE__ */ jsx(MessageHeader, { entry, t }),
|
|
41
|
+
entry.attachments?.length > 0 && /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 1, useFlexGap: true, sx: { flexWrap: "wrap" }, "aria-label": t("attachments"), role: "group", children: entry.attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
38
42
|
Chip,
|
|
39
43
|
{
|
|
40
44
|
icon: /* @__PURE__ */ jsx(AttachFileRoundedIcon, {}),
|
|
41
45
|
variant: "outlined",
|
|
42
|
-
label: attachment.size ? `${attachment.name} · ${attachment.size}` : attachment.name
|
|
46
|
+
label: attachment.size ? `${attachment.name} · ${attachment.size}` : attachment.name,
|
|
47
|
+
...onAttachmentClick && { onClick: () => onAttachmentClick(attachment, entry), "aria-label": t("openAttachment", { name: attachment.name }) }
|
|
43
48
|
},
|
|
44
49
|
attachment.name
|
|
45
50
|
)) }),
|
|
46
|
-
/* @__PURE__ */ jsx(
|
|
47
|
-
|
|
51
|
+
renderBody ? renderBody(entry) : /* @__PURE__ */ jsx(Typography, { variant: "body1", component: "div", sx: { whiteSpace: "pre-wrap", overflowWrap: "anywhere" }, children: entry.body })
|
|
52
|
+
] });
|
|
53
|
+
}
|
|
54
|
+
function MessageView({ message, actions, footer, onAttachmentClick, onExpand, renderBody, texts }) {
|
|
55
|
+
const { t } = useToolkitTranslation("inbox", texts);
|
|
56
|
+
const formatters = useFormatters();
|
|
57
|
+
const [expanded, setExpanded] = useState({ messageId: null, ids: [] });
|
|
58
|
+
if (!message) {
|
|
59
|
+
return /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", sx: { p: 4, textAlign: "center" }, children: t("noMessageSelected") });
|
|
60
|
+
}
|
|
61
|
+
const expandedIds = expanded.messageId === message.id ? expanded.ids : [];
|
|
62
|
+
const expand = (threadMessageId) => {
|
|
63
|
+
setExpanded({ messageId: message.id, ids: [...expandedIds, threadMessageId] });
|
|
64
|
+
onExpand?.(threadMessageId);
|
|
65
|
+
};
|
|
66
|
+
return /* @__PURE__ */ jsxs(Stack, { spacing: 2, component: "article", "aria-label": message.subject, children: [
|
|
67
|
+
actions && /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap", alignItems: "center" }, children: actions }),
|
|
68
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
69
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h5", component: "h2", children: message.subject }),
|
|
70
|
+
message.labels?.length > 0 && /* @__PURE__ */ jsx(Stack, { direction: "row", spacing: 0.5, useFlexGap: true, sx: { flexWrap: "wrap", mt: 1 }, children: message.labels.map((rawLabel) => {
|
|
71
|
+
const label = tagOf(rawLabel);
|
|
72
|
+
return /* @__PURE__ */ jsx(Chip, { size: "small", variant: "outlined", color: label.color, label: label.label }, label.label);
|
|
73
|
+
}) })
|
|
74
|
+
] }),
|
|
75
|
+
message.thread?.length > 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: t("threadCount", { count: message.thread.length + 1 }) }),
|
|
76
|
+
message.thread?.map((entry) => expandedIds.includes(entry.id) ? /* @__PURE__ */ jsxs(Box, { children: [
|
|
77
|
+
/* @__PURE__ */ jsx(MessageBody, { entry, t, onAttachmentClick, renderBody }),
|
|
78
|
+
/* @__PURE__ */ jsx(Divider, { sx: { mt: 2 } })
|
|
79
|
+
] }, entry.id) : /* @__PURE__ */ jsxs(
|
|
80
|
+
ButtonBase,
|
|
81
|
+
{
|
|
82
|
+
onClick: () => expand(entry.id),
|
|
83
|
+
"aria-label": t("expandMessage", { name: entry.from.name, date: inboxDateOf(entry.receivedAt, /* @__PURE__ */ new Date(), formatters) }),
|
|
84
|
+
sx: { justifyContent: "flex-start", textAlign: "left", borderRadius: 2, p: 1, mx: -1, gap: 1.5, "&:hover": { bgcolor: "action.hover" } },
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ jsx(Avatar, { sx: { width: 32, height: 32, fontSize: 13 }, children: initialsOf(entry.from.name) }),
|
|
87
|
+
/* @__PURE__ */ jsxs(Box, { sx: { minWidth: 0, flex: 1 }, children: [
|
|
88
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 600 }, noWrap: true, children: entry.from.name }),
|
|
89
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", noWrap: true, children: entry.body.replace(/\s+/g, " ").slice(0, PREVIEW_LENGTH) })
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", sx: { flexShrink: 0 }, children: inboxDateOf(entry.receivedAt, /* @__PURE__ */ new Date(), formatters) })
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
entry.id
|
|
95
|
+
)),
|
|
96
|
+
message.thread?.length > 0 && /* @__PURE__ */ jsx(Divider, {}),
|
|
97
|
+
/* @__PURE__ */ jsx(MessageBody, { entry: message, t, onAttachmentClick, renderBody }),
|
|
98
|
+
footer
|
|
48
99
|
] });
|
|
49
100
|
}
|
|
50
101
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageView.js","sources":["../../src/inbox/MessageView.jsx"],"sourcesContent":["// La lectura de un mensaje: el asunto, quién lo manda (con su correo), para quién, la fecha completa, sus archivos y el\n// texto. Arriba, las acciones que quiera el proyecto (responder, archivar, marcar como no leído, borrar).\n//\n// El texto se pinta como TEXTO, con sus saltos de renglón: nunca como HTML (un correo con HTML adentro sería un XSS).\n//\n// Genérico del paquete: function (regla de la casa).\nimport Avatar from '@mui/material/Avatar';\nimport Box from '@mui/material/Box';\nimport Chip from '@mui/material/Chip';\nimport Divider from '@mui/material/Divider';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {formatDateTime} from '@/formatting.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/**\n * @param {object} props\n * @param {{subject: string, from: {name: string, email: string}, to?: string[], receivedAt: string, body: string, attachments?: Array<{name: string, size?: string}>}|null} props.message\n * null: todavía no se elige ninguno\n * @param {import('react').ReactNode} [props.actions] los botones de arriba\n */\nexport default function MessageView({message, actions}) {\n const {t} = useToolkitTranslation('inbox');\n\n if (!message) {\n return <Typography variant=\"body2\" color=\"text.secondary\" sx={{p: 4, textAlign: 'center'}}>{t('noMessageSelected')}</Typography>;\n }\n\n return (\n <Stack spacing={2} component=\"article\" aria-label={message.subject}>\n {actions && <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{flexWrap: 'wrap'}}>{actions}</Stack>}\n <Typography variant=\"h5\" component=\"h2\">{message.subject}</Typography>\n <Stack direction=\"row\" spacing={1.5} sx={{alignItems: 'center'}}>\n <Avatar>{initialsOf(message.from.name)}</Avatar>\n <Box sx={{minWidth: 0, flex: 1}}>\n <Typography variant=\"subtitle2\" component=\"p\" noWrap>\n {message.from.name} <Typography component=\"span\" variant=\"body2\" color=\"text.secondary\"><{message.from.email}></Typography>\n </Typography>\n {message.to?.length > 0 && <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{t('to', {to: message.to.join(', ')})}</Typography>}\n {/* la fecha debajo y no a la derecha: en el teléfono apretaba el «Para» a dos renglones */}\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{formatDateTime(message.receivedAt)}</Typography>\n </Box>\n </Stack>\n {message.attachments?.length > 0 && (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{flexWrap: 'wrap'}} aria-label={t('attachments')}>\n {message.attachments.map((attachment) => (\n <Chip key={attachment.name} icon={<AttachFileRoundedIcon />} variant=\"outlined\"\n label={attachment.size ? `${attachment.name} · ${attachment.size}` : attachment.name} />\n ))}\n </Stack>\n )}\n <Divider />\n <Typography variant=\"body1\" component=\"div\" sx={{whiteSpace: 'pre-wrap', overflowWrap: 'anywhere'}}>{message.body}</Typography>\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAwBA,SAAwB,YAAY,EAAC,SAAS,WAAU;AACpD,QAAM,EAAC,EAAA,IAAK,sBAAsB,OAAO;AAEzC,MAAI,CAAC,SAAS;AACV,WAAO,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,IAAI,EAAC,GAAG,GAAG,WAAW,SAAA,GAAY,UAAA,EAAE,mBAAmB,GAAE;AAAA,EACvH;AAEA,SACI,qBAAC,SAAM,SAAS,GAAG,WAAU,WAAU,cAAY,QAAQ,SACtD,UAAA;AAAA,IAAA,WAAW,oBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,KAAK,YAAU,MAAC,IAAI,EAAC,UAAU,OAAA,GAAU,UAAA,SAAQ;AAAA,wBAC5F,YAAA,EAAW,SAAQ,MAAK,WAAU,MAAM,kBAAQ,SAAQ;AAAA,IACzD,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,KAAK,IAAI,EAAC,YAAY,SAAA,GAClD,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAQ,UAAA,WAAW,QAAQ,KAAK,IAAI,GAAE;AAAA,MACvC,qBAAC,OAAI,IAAI,EAAC,UAAU,GAAG,MAAM,KACzB,UAAA;AAAA,QAAA,qBAAC,cAAW,SAAQ,aAAY,WAAU,KAAI,QAAM,MAC/C,UAAA;AAAA,UAAA,QAAQ,KAAK;AAAA,UAAK;AAAA,+BAAE,YAAA,EAAW,WAAU,QAAO,SAAQ,SAAQ,OAAM,kBAAiB,UAAA;AAAA,YAAA;AAAA,YAAK,QAAQ,KAAK;AAAA,YAAM;AAAA,UAAA,EAAA,CAAI;AAAA,QAAA,GACxH;AAAA,QACC,QAAQ,IAAI,SAAS,yBAAM,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAK,UAAA,EAAE,MAAM,EAAC,IAAI,QAAQ,GAAG,KAAK,IAAI,EAAA,CAAE,EAAA,CAAE;AAAA,QAEpI,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAK,UAAA,eAAe,QAAQ,UAAU,EAAA,CAAE;AAAA,MAAA,EAAA,CAC3G;AAAA,IAAA,GACJ;AAAA,IACC,QAAQ,aAAa,SAAS,KAC3B,oBAAC,SAAM,WAAU,OAAM,SAAS,GAAG,YAAU,MAAC,IAAI,EAAC,UAAU,OAAA,GAAS,cAAY,EAAE,aAAa,GAC5F,UAAA,QAAQ,YAAY,IAAI,CAAC,eACtB;AAAA,MAAC;AAAA,MAAA;AAAA,QAA2B,0BAAO,uBAAA,EAAsB;AAAA,QAAI,SAAQ;AAAA,QAC/D,OAAO,WAAW,OAAO,GAAG,WAAW,IAAI,MAAM,WAAW,IAAI,KAAK,WAAW;AAAA,MAAA;AAAA,MAD3E,WAAW;AAAA,IAAA,CAEzB,GACL;AAAA,wBAEH,SAAA,EAAQ;AAAA,IACT,oBAAC,YAAA,EAAW,SAAQ,SAAQ,WAAU,OAAM,IAAI,EAAC,YAAY,YAAY,cAAc,WAAA,GAAc,kBAAQ,KAAA,CAAK;AAAA,EAAA,GACtH;AAER;"}
|
|
1
|
+
{"version":3,"file":"MessageView.js","sources":["../../src/inbox/MessageView.jsx"],"sourcesContent":["// La lectura de un mensaje o de una conversación completa: el asunto con sus etiquetas y, debajo, cada mensaje de la\n// conversación (los anteriores encogidos en un renglón con quién y cuándo, como Gmail; al picarlos se abren). De cada uno:\n// quién lo manda (con su correo), para quién y con copia a quién, la fecha completa, sus archivos y el texto. Arriba, las\n// acciones que quiera el proyecto (responder, archivar, posponer, borrar); abajo, lo que quiera poner (una respuesta rápida).\n//\n// El texto se pinta como TEXTO, con sus saltos de renglón: nunca como HTML (un correo con HTML adentro sería un XSS). Si un\n// proyecto necesita HTML, lo pinta con renderBody y es SU responsabilidad limpiarlo antes (DOMPurify o su backend).\n//\n// Todo hacia afuera: onAttachmentClick al picar un archivo (descargarlo, verlo), onExpand al abrir un mensaje anterior (para\n// marcarlo como leído), renderBody, footer y texts.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Avatar from '@mui/material/Avatar';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Divider from '@mui/material/Divider';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {inboxDateOf} from '@/inbox/inboxDates.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {tagOf} from '@/ui/labels/tagOf.js';\n\nconst PREVIEW_LENGTH = 90;\n\n/** Quién manda, para quién, con copia a quién y cuándo. */\nfunction MessageHeader({entry, t}) {\n const {formatDateTime} = useFormatters();\n return (\n <Stack direction=\"row\" spacing={1.5} sx={{alignItems: 'center'}}>\n <Avatar>{initialsOf(entry.from.name)}</Avatar>\n <Box sx={{minWidth: 0, flex: 1}}>\n <Typography variant=\"subtitle2\" component=\"p\" noWrap>\n {entry.from.name} <Typography component=\"span\" variant=\"body2\" color=\"text.secondary\"><{entry.from.email}></Typography>\n </Typography>\n {entry.to?.length > 0 && <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{t('to', {to: entry.to.join(', ')})}</Typography>}\n {entry.cc?.length > 0 && <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{t('cc', {cc: entry.cc.join(', ')})}</Typography>}\n {/* la fecha debajo y no a la derecha: en el teléfono apretaba el «Para» a dos renglones */}\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{formatDateTime(entry.receivedAt)}</Typography>\n </Box>\n </Stack>\n );\n}\n\n/** Un mensaje abierto: su encabezado, sus archivos y su texto. */\nfunction MessageBody({entry, t, onAttachmentClick, renderBody}) {\n return (\n <Stack spacing={2}>\n <MessageHeader entry={entry} t={t} />\n {entry.attachments?.length > 0 && (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{flexWrap: 'wrap'}} aria-label={t('attachments')} role=\"group\">\n {entry.attachments.map((attachment) => (\n <Chip key={attachment.name} icon={<AttachFileRoundedIcon />} variant=\"outlined\"\n label={attachment.size ? `${attachment.name} · ${attachment.size}` : attachment.name}\n {...(onAttachmentClick && {onClick: () => onAttachmentClick(attachment, entry), 'aria-label': t('openAttachment', {name: attachment.name})})} />\n ))}\n </Stack>\n )}\n {renderBody ? renderBody(entry) : (\n <Typography variant=\"body1\" component=\"div\" sx={{whiteSpace: 'pre-wrap', overflowWrap: 'anywhere'}}>{entry.body}</Typography>\n )}\n </Stack>\n );\n}\n\n/**\n * @param {object} props\n * @param {{id?: string, subject: string, from: {name: string, email: string}, to?: string[], cc?: string[], receivedAt: string, body: string, attachments?: Array<{name: string, size?: string} & Record<string, any>>, labels?: Array<string|{label: string, color?: string}>, thread?: Array<{id: string, from: {name: string, email: string}, to?: string[], cc?: string[], receivedAt: string, body: string, attachments?: Array<{name: string, size?: string}>}>}|null} props.message\n * el mensaje más nuevo de la conversación; thread: los anteriores, del más viejo al más nuevo (encogidos). null: todavía\n * no se elige ninguno\n * @param {import('react').ReactNode} [props.actions] los botones de arriba\n * @param {import('react').ReactNode} [props.footer] lo de abajo (una respuesta rápida, firmas…)\n * @param {(attachment: any, message: any) => void} [props.onAttachmentClick] al picar un archivo; sin él los archivos solo se ven\n * @param {(threadMessageId: string) => void} [props.onExpand] al abrir un mensaje anterior de la conversación\n * @param {(message: any) => import('react').ReactNode} [props.renderBody] el texto a su manera (HTML ya limpio por el proyecto)\n * @param {object} [props.texts] los textos que cambian, con la forma de `inbox` en los textos del paquete\n */\nexport default function MessageView({message, actions, footer, onAttachmentClick, onExpand, renderBody, texts}) {\n const {t} = useToolkitTranslation('inbox', texts);\n const formatters = useFormatters();\n // los mensajes anteriores que la persona abrió (empiezan encogidos), de qué conversación: al cambiar de mensaje se olvidan\n const [expanded, setExpanded] = useState({messageId: null, ids: []});\n\n if (!message) {\n return <Typography variant=\"body2\" color=\"text.secondary\" sx={{p: 4, textAlign: 'center'}}>{t('noMessageSelected')}</Typography>;\n }\n\n const expandedIds = expanded.messageId === message.id ? expanded.ids : [];\n const expand = (threadMessageId) => {\n setExpanded({messageId: message.id, ids: [...expandedIds, threadMessageId]});\n onExpand?.(threadMessageId);\n };\n\n return (\n <Stack spacing={2} component=\"article\" aria-label={message.subject}>\n {actions && <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{flexWrap: 'wrap', alignItems: 'center'}}>{actions}</Stack>}\n <Box>\n <Typography variant=\"h5\" component=\"h2\">{message.subject}</Typography>\n {message.labels?.length > 0 && (\n <Stack direction=\"row\" spacing={0.5} useFlexGap sx={{flexWrap: 'wrap', mt: 1}}>\n {message.labels.map((rawLabel) => {\n const label = tagOf(rawLabel);\n return <Chip key={label.label} size=\"small\" variant=\"outlined\" color={label.color} label={label.label} />;\n })}\n </Stack>\n )}\n </Box>\n {message.thread?.length > 0 && (\n <Typography variant=\"caption\" color=\"text.secondary\">{t('threadCount', {count: message.thread.length + 1})}</Typography>\n )}\n {/* los anteriores: encogidos en un renglón; abiertos, completos y con su línea abajo */}\n {message.thread?.map((entry) => (expandedIds.includes(entry.id) ? (\n <Box key={entry.id}>\n <MessageBody entry={entry} t={t} onAttachmentClick={onAttachmentClick} renderBody={renderBody} />\n <Divider sx={{mt: 2}} />\n </Box>\n ) : (\n <ButtonBase key={entry.id} onClick={() => expand(entry.id)} aria-label={t('expandMessage', {name: entry.from.name, date: inboxDateOf(entry.receivedAt, new Date(), formatters)})}\n sx={{justifyContent: 'flex-start', textAlign: 'left', borderRadius: 2, p: 1, mx: -1, gap: 1.5, '&:hover': {bgcolor: 'action.hover'}}}>\n <Avatar sx={{width: 32, height: 32, fontSize: 13}}>{initialsOf(entry.from.name)}</Avatar>\n <Box sx={{minWidth: 0, flex: 1}}>\n <Typography variant=\"body2\" sx={{fontWeight: 600}} noWrap>{entry.from.name}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{entry.body.replace(/\\s+/g, ' ').slice(0, PREVIEW_LENGTH)}</Typography>\n </Box>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{flexShrink: 0}}>{inboxDateOf(entry.receivedAt, new Date(), formatters)}</Typography>\n </ButtonBase>\n )))}\n {message.thread?.length > 0 && <Divider />}\n <MessageBody entry={message} t={t} onAttachmentClick={onAttachmentClick} renderBody={renderBody} />\n {footer}\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA4BA,MAAM,iBAAiB;AAGvB,SAAS,cAAc,EAAC,OAAO,KAAI;AAC/B,QAAM,EAAC,eAAA,IAAkB,cAAA;AACzB,SACI,qBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,KAAK,IAAI,EAAC,YAAY,SAAA,GAClD,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAQ,UAAA,WAAW,MAAM,KAAK,IAAI,GAAE;AAAA,IACrC,qBAAC,OAAI,IAAI,EAAC,UAAU,GAAG,MAAM,KACzB,UAAA;AAAA,MAAA,qBAAC,cAAW,SAAQ,aAAY,WAAU,KAAI,QAAM,MAC/C,UAAA;AAAA,QAAA,MAAM,KAAK;AAAA,QAAK;AAAA,6BAAE,YAAA,EAAW,WAAU,QAAO,SAAQ,SAAQ,OAAM,kBAAiB,UAAA;AAAA,UAAA;AAAA,UAAK,MAAM,KAAK;AAAA,UAAM;AAAA,QAAA,EAAA,CAAI;AAAA,MAAA,GACpH;AAAA,MACC,MAAM,IAAI,SAAS,yBAAM,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAK,UAAA,EAAE,MAAM,EAAC,IAAI,MAAM,GAAG,KAAK,IAAI,EAAA,CAAE,EAAA,CAAE;AAAA,MAC/H,MAAM,IAAI,SAAS,yBAAM,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAK,UAAA,EAAE,MAAM,EAAC,IAAI,MAAM,GAAG,KAAK,IAAI,EAAA,CAAE,EAAA,CAAE;AAAA,MAEhI,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,WAAU,KAAK,UAAA,eAAe,MAAM,UAAU,EAAA,CAAE;AAAA,IAAA,EAAA,CACzG;AAAA,EAAA,GACJ;AAER;AAGA,SAAS,YAAY,EAAC,OAAO,GAAG,mBAAmB,cAAa;AAC5D,SACI,qBAAC,OAAA,EAAM,SAAS,GACZ,UAAA;AAAA,IAAA,oBAAC,eAAA,EAAc,OAAc,EAAA,CAAM;AAAA,IAClC,MAAM,aAAa,SAAS,KACzB,oBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,GAAG,YAAU,MAAC,IAAI,EAAC,UAAU,OAAA,GAAS,cAAY,EAAE,aAAa,GAAG,MAAK,SACpG,UAAA,MAAM,YAAY,IAAI,CAAC,eACpB;AAAA,MAAC;AAAA,MAAA;AAAA,QAA2B,0BAAO,uBAAA,EAAsB;AAAA,QAAI,SAAQ;AAAA,QAC/D,OAAO,WAAW,OAAO,GAAG,WAAW,IAAI,MAAM,WAAW,IAAI,KAAK,WAAW;AAAA,QAC/E,GAAI,qBAAqB,EAAC,SAAS,MAAM,kBAAkB,YAAY,KAAK,GAAG,cAAc,EAAE,kBAAkB,EAAC,MAAM,WAAW,KAAA,CAAK,EAAA;AAAA,MAAC;AAAA,MAFrI,WAAW;AAAA,IAAA,CAGzB,GACL;AAAA,IAEH,aAAa,WAAW,KAAK,IAC1B,oBAAC,YAAA,EAAW,SAAQ,SAAQ,WAAU,OAAM,IAAI,EAAC,YAAY,YAAY,cAAc,cAAc,gBAAM,KAAA,CAAK;AAAA,EAAA,GAExH;AAER;AAcA,SAAwB,YAAY,EAAC,SAAS,SAAS,QAAQ,mBAAmB,UAAU,YAAY,SAAQ;AAC5G,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS,KAAK;AAChD,QAAM,aAAa,cAAA;AAEnB,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAC,WAAW,MAAM,KAAK,CAAA,GAAG;AAEnE,MAAI,CAAC,SAAS;AACV,WAAO,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,IAAI,EAAC,GAAG,GAAG,WAAW,SAAA,GAAY,UAAA,EAAE,mBAAmB,GAAE;AAAA,EACvH;AAEA,QAAM,cAAc,SAAS,cAAc,QAAQ,KAAK,SAAS,MAAM,CAAA;AACvE,QAAM,SAAS,CAAC,oBAAoB;AAChC,gBAAY,EAAC,WAAW,QAAQ,IAAI,KAAK,CAAC,GAAG,aAAa,eAAe,GAAE;AAC3E,eAAW,eAAe;AAAA,EAC9B;AAEA,SACI,qBAAC,SAAM,SAAS,GAAG,WAAU,WAAU,cAAY,QAAQ,SACtD,UAAA;AAAA,IAAA,WAAW,oBAAC,OAAA,EAAM,WAAU,OAAM,SAAS,KAAK,YAAU,MAAC,IAAI,EAAC,UAAU,QAAQ,YAAY,YAAY,UAAA,SAAQ;AAAA,yBAClH,KAAA,EACG,UAAA;AAAA,MAAA,oBAAC,cAAW,SAAQ,MAAK,WAAU,MAAM,kBAAQ,SAAQ;AAAA,MACxD,QAAQ,QAAQ,SAAS,yBACrB,OAAA,EAAM,WAAU,OAAM,SAAS,KAAK,YAAU,MAAC,IAAI,EAAC,UAAU,QAAQ,IAAI,EAAA,GACtE,UAAA,QAAQ,OAAO,IAAI,CAAC,aAAa;AAC9B,cAAM,QAAQ,MAAM,QAAQ;AAC5B,eAAO,oBAAC,MAAA,EAAuB,MAAK,SAAQ,SAAQ,YAAW,OAAO,MAAM,OAAO,OAAO,MAAM,MAAA,GAA9E,MAAM,KAA+E;AAAA,MAC3G,CAAC,EAAA,CACL;AAAA,IAAA,GAER;AAAA,IACC,QAAQ,QAAQ,SAAS,KACtB,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAkB,UAAA,EAAE,eAAe,EAAC,OAAO,QAAQ,OAAO,SAAS,EAAA,CAAE,GAAE;AAAA,IAG9G,QAAQ,QAAQ,IAAI,CAAC,UAAW,YAAY,SAAS,MAAM,EAAE,IAC1D,qBAAC,KAAA,EACG,UAAA;AAAA,MAAA,oBAAC,aAAA,EAAY,OAAc,GAAM,mBAAsC,YAAwB;AAAA,0BAC9F,SAAA,EAAQ,IAAI,EAAC,IAAI,IAAC,CAAG;AAAA,IAAA,KAFhB,MAAM,EAGhB,IAEA;AAAA,MAAC;AAAA,MAAA;AAAA,QAA0B,SAAS,MAAM,OAAO,MAAM,EAAE;AAAA,QAAG,cAAY,EAAE,iBAAiB,EAAC,MAAM,MAAM,KAAK,MAAM,MAAM,YAAY,MAAM,YAAY,oBAAI,QAAQ,UAAU,GAAE;AAAA,QACnK,IAAI,EAAC,gBAAgB,cAAc,WAAW,QAAQ,cAAc,GAAG,GAAG,GAAG,IAAI,IAAI,KAAK,KAAK,WAAW,EAAC,SAAS,iBAAc;AAAA,QAC1I,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAO,IAAI,EAAC,OAAO,IAAI,QAAQ,IAAI,UAAU,GAAA,GAAM,UAAA,WAAW,MAAM,KAAK,IAAI,GAAE;AAAA,UAChF,qBAAC,OAAI,IAAI,EAAC,UAAU,GAAG,MAAM,KACzB,UAAA;AAAA,YAAA,oBAAC,YAAA,EAAW,SAAQ,SAAQ,IAAI,EAAC,YAAY,IAAA,GAAM,QAAM,MAAE,UAAA,MAAM,KAAK,MAAK;AAAA,gCAC1E,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,QAAM,MAAE,UAAA,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,MAAM,GAAG,cAAc,EAAA,CAAE;AAAA,UAAA,GACxH;AAAA,8BACC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAiB,IAAI,EAAC,YAAY,KAAK,sBAAY,MAAM,gCAAgB,KAAA,GAAQ,UAAU,EAAA,CAAE;AAAA,QAAA;AAAA,MAAA;AAAA,MAPpH,MAAM;AAAA,IAAA,CASzB;AAAA,IACD,QAAQ,QAAQ,SAAS,yBAAM,SAAA,EAAQ;AAAA,wBACvC,aAAA,EAAY,OAAO,SAAS,GAAM,mBAAsC,YAAwB;AAAA,IAChG;AAAA,EAAA,GACL;AAER;"}
|
package/dist/inbox/inboxDates.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
function inboxDateOf(receivedAt, now = /* @__PURE__ */ new Date()) {
|
|
3
|
-
|
|
1
|
+
import { formatTime, formatDate } from "../formatting.js";
|
|
2
|
+
function inboxDateOf(receivedAt, now = /* @__PURE__ */ new Date(), formatters = { formatDate, formatTime }) {
|
|
3
|
+
const isToday = formatters.formatDate(receivedAt) === formatters.formatDate(now.toISOString());
|
|
4
|
+
return isToday ? formatters.formatTime(receivedAt) : formatters.formatDate(receivedAt);
|
|
4
5
|
}
|
|
5
6
|
export {
|
|
6
7
|
inboxDateOf
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inboxDates.js","sources":["../../src/inbox/inboxDates.js"],"sourcesContent":["// La fecha corta de un mensaje en la lista, como la pintan los correos: si llegó hoy, la hora («09:14»); si no, el día\n// («14 oct 2026»). «Hoy» es en la zona del negocio (la de ToolkitProvider), no la del servidor.\n//\n// Función pura
|
|
1
|
+
{"version":3,"file":"inboxDates.js","sources":["../../src/inbox/inboxDates.js"],"sourcesContent":["// La fecha corta de un mensaje en la lista, como la pintan los correos: si llegó hoy, la hora («09:14»); si no, el día\n// («14 oct 2026»). «Hoy» es en la zona del negocio (la de ToolkitProvider), no la del servidor.\n//\n// Función pura: function (regla de la casa).\nimport {formatDate, formatTime} from '@/formatting.js';\n\n/**\n * @param {string} receivedAt el instante ISO\n * @param {Date} [now] para las pruebas; por omisión, ahora\n * @param {{formatDate: Function, formatTime: Function}} [formatters] los de useFormatters (el proveedor más cercano); por\n * omisión los sueltos (el de la raíz)\n * @returns {string}\n */\nexport function inboxDateOf(receivedAt, now = new Date(), formatters = {formatDate, formatTime}) {\n const isToday = formatters.formatDate(receivedAt) === formatters.formatDate(now.toISOString());\n return isToday ? formatters.formatTime(receivedAt) : formatters.formatDate(receivedAt);\n}\n"],"names":[],"mappings":";AAaO,SAAS,YAAY,YAAY,MAAM,oBAAI,KAAI,GAAI,aAAa,EAAC,YAAY,WAAU,GAAG;AAC7F,QAAM,UAAU,WAAW,WAAW,UAAU,MAAM,WAAW,WAAW,IAAI,aAAa;AAC7F,SAAO,UAAU,WAAW,WAAW,UAAU,IAAI,WAAW,WAAW,UAAU;AACzF;"}
|