@stackcraftmx/calcita 0.27.0 → 0.30.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 +149 -0
- package/dist/DataTable/AuditTrailDialog.js +18 -14
- package/dist/DataTable/AuditTrailDialog.js.map +1 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +12 -6
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +8 -3
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +26 -18
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
- package/dist/DataTable/EmptyState.js +8 -3
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +11 -5
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +9 -4
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +74 -61
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/columns.js +1 -8
- package/dist/DataTable/columns.js.map +1 -1
- package/dist/access/PermissionMatrix.js +18 -9
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/account/AccountSecuritySection.js +8 -3
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/DemoTotpCodeButton.js +8 -3
- package/dist/account/DemoTotpCodeButton.js.map +1 -1
- package/dist/account/ProfileScreen.js +19 -13
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +6 -2
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/appShell/AccountMenu.js +13 -5
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +100 -71
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +21 -13
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +26 -13
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActions.js +10 -5
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarCapsule.js +9 -4
- package/dist/appShell/BarCapsule.js.map +1 -1
- package/dist/appShell/BarSearch.js +7 -3
- package/dist/appShell/BarSearch.js.map +1 -1
- package/dist/appShell/HeaderTools.js +20 -15
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MobileTabBar.js +25 -14
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +7 -3
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +53 -35
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +18 -9
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +6 -2
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TabBarEditor.js +16 -7
- package/dist/appShell/TabBarEditor.js.map +1 -1
- package/dist/appShell/TopMenu.js +12 -7
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +6 -2
- package/dist/appShell/Wordmark.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +14 -7
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +26 -19
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +28 -13
- package/dist/calendar/EventDetails.js.map +1 -1
- package/dist/downloads/DownloadManager.js +34 -18
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/feedback/ConfirmDialog.js +22 -17
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +12 -5
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +16 -8
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +39 -31
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/forms/FormProblemAlert.js +4 -1
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +6 -2
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/security/SecondStepForm.js +6 -2
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +19 -9
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/hookForm/FormProblemAlert.js +4 -1
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/inbox/InboxLayout.js +47 -29
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +53 -32
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +31 -15
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/inputs/ColorInput.js +22 -17
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FormSection.js +66 -63
- package/dist/inputs/FormSection.js.map +1 -1
- package/dist/inputs/SelectInput.js +21 -16
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +25 -20
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +21 -15
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/kanban/KanbanBoard.js +82 -50
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/maps/MapView.js +19 -14
- package/dist/maps/MapView.js.map +1 -1
- package/dist/pwa/InstallAppSetting.js +4 -1
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +6 -2
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +10 -5
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/security/ActivityLog.js +6 -2
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +8 -3
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +12 -5
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/theme/components/glassComponents.js +19 -16
- package/dist/theme/components/glassComponents.js.map +1 -1
- package/dist/theme/components/minimalComponents.js +10 -6
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/theme/createToolkitTheme.js +23 -6
- package/dist/theme/createToolkitTheme.js.map +1 -1
- package/dist/theme/cssTokens.js +23 -0
- package/dist/theme/cssTokens.js.map +1 -0
- package/dist/theme/glassSurface.js +64 -10
- package/dist/theme/glassSurface.js.map +1 -1
- package/dist/theme/glassToasterStyles.js +2 -3
- package/dist/theme/glassToasterStyles.js.map +1 -1
- package/dist/theme/themePieces.js +16 -0
- package/dist/theme/themePieces.js.map +1 -0
- package/dist/types/DataTable/AuditTrailDialog.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +1 -1
- package/dist/types/DataTable/EmptyState.d.ts +1 -1
- package/dist/types/DataTable/FilterChips.d.ts +1 -1
- package/dist/types/DataTable/GoToPage.d.ts +1 -1
- package/dist/types/DataTable/ServerTable.d.ts +1 -1
- package/dist/types/access/PermissionMatrix.d.ts +1 -1
- package/dist/types/account/AccountSecuritySection.d.ts +1 -1
- package/dist/types/account/DemoTotpCodeButton.d.ts +1 -1
- package/dist/types/account/ProfileScreen.d.ts +1 -1
- package/dist/types/account/SignInScreen.d.ts +1 -1
- package/dist/types/appShell/AccountMenu.d.ts +1 -1
- package/dist/types/appShell/AppLayout.d.ts +1 -1
- package/dist/types/appShell/AppearanceSettings.d.ts +1 -1
- package/dist/types/appShell/AuthLayout.d.ts +1 -1
- package/dist/types/appShell/BarActions.d.ts +1 -1
- package/dist/types/appShell/BarCapsule.d.ts +1 -1
- package/dist/types/appShell/BarSearch.d.ts +1 -1
- package/dist/types/appShell/HeaderTools.d.ts +1 -1
- package/dist/types/appShell/MobileTabBar.d.ts +1 -1
- package/dist/types/appShell/SettingsMenu.d.ts +1 -1
- package/dist/types/appShell/SideMenu.d.ts +1 -1
- package/dist/types/appShell/SideMenuEntry.d.ts +1 -1
- package/dist/types/appShell/SubmenuActionsMenu.d.ts +1 -1
- package/dist/types/appShell/TabBarEditor.d.ts +1 -1
- package/dist/types/appShell/TopMenu.d.ts +1 -1
- package/dist/types/appShell/Wordmark.d.ts +1 -1
- package/dist/types/auth/PasswordChecklist.d.ts +1 -1
- package/dist/types/calendar/EventCalendar.d.ts +1 -1
- package/dist/types/calendar/EventDetails.d.ts +1 -1
- package/dist/types/feedback/ConfirmDialog.d.ts +1 -1
- package/dist/types/feedback/ModalWaiting.d.ts +1 -1
- package/dist/types/feedback/NewBuildNotice.d.ts +1 -1
- package/dist/types/filepond/FileDropzone.d.ts +1 -1
- package/dist/types/forms/auth/SignInForm.d.ts +1 -1
- package/dist/types/forms/security/SecondStepForm.d.ts +1 -1
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +1 -1
- package/dist/types/inbox/InboxLayout.d.ts +1 -1
- package/dist/types/inbox/MessageList.d.ts +1 -1
- package/dist/types/inbox/MessageView.d.ts +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/inputs/ColorInput.d.ts +1 -1
- package/dist/types/inputs/FormSection.d.ts +2 -2
- package/dist/types/inputs/SelectInput.d.ts +1 -1
- package/dist/types/inputs/TextInput.d.ts +1 -1
- package/dist/types/inputs/WizardLayout.d.ts +1 -1
- package/dist/types/kanban/KanbanBoard.d.ts +1 -1
- package/dist/types/maps/MapView.d.ts +1 -1
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +1 -1
- package/dist/types/pwa/InstallPromptBanner.d.ts +1 -1
- package/dist/types/security/ActivityLog.d.ts +1 -1
- package/dist/types/security/RecoveryCodesDialog.d.ts +1 -1
- package/dist/types/security/SessionsCard.d.ts +1 -1
- package/dist/types/theme/createToolkitTheme.d.ts +33 -1
- package/dist/types/theme/cssTokens.d.ts +46 -0
- package/dist/types/theme/glassSurface.d.ts +57 -1
- package/dist/types/theme/themePieces.d.ts +20 -0
- package/dist/types/ui/actions/ActionsMenu.d.ts +1 -1
- package/dist/types/ui/actions/DownloadMenu.d.ts +1 -1
- package/dist/types/ui/actions/SelectionActions.d.ts +1 -1
- package/dist/types/ui/filters/DateRangeFilter.d.ts +1 -1
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +1 -1
- package/dist/types/ui/filters/QuickFilters.d.ts +1 -1
- package/dist/types/ui/filters/SegmentedControl.d.ts +7 -2
- package/dist/types/ui/labels/StatusChip.d.ts +1 -1
- package/dist/types/ui/labels/Tag.d.ts +1 -1
- package/dist/types/ui/layout/CollapsibleSection.d.ts +1 -1
- package/dist/types/ui/layout/ContentDialog.d.ts +1 -1
- package/dist/types/ui/layout/DefinitionList.d.ts +1 -1
- package/dist/types/ui/layout/DetailHeader.d.ts +1 -1
- package/dist/types/ui/layout/ErrorPage.d.ts +1 -1
- package/dist/types/ui/layout/GlassPanel.d.ts +1 -1
- package/dist/types/ui/layout/PageHeader.d.ts +1 -1
- package/dist/types/ui/layout/PageHeading.d.ts +1 -1
- package/dist/types/ui/layout/ProfileHeader.d.ts +1 -1
- package/dist/types/ui/layout/ScrollableRow.d.ts +1 -1
- package/dist/types/ui/layout/SectionCard.d.ts +1 -1
- package/dist/types/ui/metrics/BarList.d.ts +1 -1
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +1 -1
- package/dist/types/ui/metrics/ChartTooltip.d.ts +1 -1
- package/dist/types/ui/metrics/ColumnChart.d.ts +1 -1
- package/dist/types/ui/metrics/LineChart.d.ts +1 -1
- package/dist/types/ui/metrics/MetricCard.d.ts +1 -1
- package/dist/types/ui/metrics/TableToggle.d.ts +1 -1
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +1 -1
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +1 -1
- package/dist/types/ui/navigation/LanguageToggle.d.ts +1 -1
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +1 -1
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +1 -1
- package/dist/types/upload/FileDropzone.d.ts +1 -1
- package/dist/ui/actions/ActionsMenu.js +9 -4
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +21 -17
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +15 -7
- package/dist/ui/actions/SelectionActions.js.map +1 -1
- package/dist/ui/filters/DateRangeFilter.js +11 -5
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/PeriodRangeSelector.js +11 -5
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +16 -8
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/filters/SegmentedControl.js +12 -5
- package/dist/ui/filters/SegmentedControl.js.map +1 -1
- package/dist/ui/labels/StatusChip.js +35 -25
- package/dist/ui/labels/StatusChip.js.map +1 -1
- package/dist/ui/labels/Tag.js +8 -3
- package/dist/ui/labels/Tag.js.map +1 -1
- package/dist/ui/layout/CollapsibleSection.js +18 -8
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +13 -6
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/DefinitionList.js +13 -6
- package/dist/ui/layout/DefinitionList.js.map +1 -1
- package/dist/ui/layout/DetailHeader.js +14 -6
- package/dist/ui/layout/DetailHeader.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +20 -15
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +4 -1
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/GlassPanel.js +8 -4
- package/dist/ui/layout/GlassPanel.js.map +1 -1
- package/dist/ui/layout/PageHeader.js +10 -4
- package/dist/ui/layout/PageHeader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +17 -8
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/layout/ProfileHeader.js +21 -10
- package/dist/ui/layout/ProfileHeader.js.map +1 -1
- package/dist/ui/layout/ScrollableRow.js +19 -15
- package/dist/ui/layout/ScrollableRow.js.map +1 -1
- package/dist/ui/layout/SectionCard.js +10 -4
- package/dist/ui/layout/SectionCard.js.map +1 -1
- package/dist/ui/metrics/BarList.js +9 -4
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +40 -21
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js +6 -2
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +22 -18
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +20 -16
- package/dist/ui/metrics/LineChart.js.map +1 -1
- package/dist/ui/metrics/MetricCard.js +42 -24
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +7 -2
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +14 -5
- package/dist/ui/metrics/chartParts.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +8 -4
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
- package/dist/ui/navigation/FontSizeToggle.js +6 -2
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
- package/dist/ui/navigation/LanguageToggle.js +8 -4
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +12 -6
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +26 -13
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/FileDropzone.js +35 -25
- package/dist/upload/FileDropzone.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 (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":";;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;AAkBtB,SAAwB,YAAY;AAAA,EAChC;AAAA,EAAU,aAAa;AAAA,EAAM;AAAA,EAAU;AAAA,EAAc,cAAc,CAAA;AAAA,EAAI;AAAA,EAAmB;AAAA,EAAgB;AAAA,EAAe;AAAA,EAAW;AACxI,GAAG;AACC,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS,KAAK;AAChD,QAAM,aAAa,cAAA;AAEnB,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,QAAM,iBAAiB,CAAC,WAAW,cAAc;AAC7C,sBAAkB,YAAY,CAAC,GAAG,aAAa,SAAS,IAAI,YAAY,OAAO,CAAC,OAAO,OAAO,SAAS,CAAC;AAAA,EAC5G;AAEA,SACI,oBAAC,MAAA,EAAK,gBAAc,MAAC,cAAY,EAAE,WAAW,GACzC,UAAA,SAAS,IAAI,CAAC,YAAY;AACvB,UAAM,SAAS,QAAQ,WAAW,MAAM;AACxC,UAAM,YAAY,YAAY,SAAS,QAAQ,EAAE;AACjD,UAAM,SAAS,oBAAC,QAAA,EAAO,IAAI,EAAC,OAAO,eAAe,QAAQ,cAAA,GAAiB,UAAA,WAAW,QAAQ,KAAK,IAAI,GAAE;AACzG,WACI,oBAAC,UAAA,EAA0B,gBAAc,MAAC,kBAAkB,gBAAgB,mBACxE,qBAAC,OAAA,EAAM,WAAU,OAAM,IAAI,EAAC,YAAY,YACnC,UAAA;AAAA,MAAA,gBACG;AAAA,QAAC;AAAA,QAAA;AAAA,UAAW,MAAK;AAAA,UAAQ,SAAS,MAAM,aAAa,QAAQ,EAAE;AAAA,UAAG,gBAAc,QAAQ,QAAQ,SAAS;AAAA,UAC7F,cAAY,QAAQ,YAAY,EAAE,QAAQ,IAAI,EAAE,MAAM;AAAA,UAAG,IAAI,EAAC,OAAO,QAAQ,YAAY,iBAAiB,iBAAA;AAAA,UACjH,UAAA,QAAQ,YAAY,oBAAC,iBAAA,EAAgB,UAAS,SAAQ,IAAK,oBAAC,uBAAA,EAAsB,UAAS,QAAA,CAAQ;AAAA,QAAA;AAAA,MAAA;AAAA,MAG3G,kBAAkB,oBAAC,aAAA,EAAY,OAAO,EAAE,kBAAkB,EAAC,SAAS,QAAQ,SAAQ,GAAG,SAAS,eAAe,OAAO,EAAA,CAAG;AAAA,IAAA,GAC9H,GACD,IAAI,EAAC,sCAAsC,EAAC,OAAO,EAAA,KAClD,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAe,UAAU,QAAQ,OAAO,cAAc;AAAA,QAAW,SAAS,MAAM,SAAS,QAAQ,EAAE;AAAA,QAAG,YAAW;AAAA,QAClG,IAAI,EAAC,cAAc,GAAG,IAAI,gBAAgB,iBAAiB,KAAK,EAAA;AAAA,QAC5E,UAAA;AAAA,UAAA,oBAAC,kBAAe,IAAI,EAAC,IAAI,IAAA,GAIpB,UAAA,oBACG;AAAA,YAAC;AAAA,YAAA;AAAA,cAAW,MAAK;AAAA,cAAW,gBAAc;AAAA,cAAW,cAAY,EAAE,UAAU,EAAC,SAAS,QAAQ,SAAQ;AAAA,cAC3F,IAAI,EAAC,cAAc,MAAA;AAAA,cACnB,SAAS,CAAC,UAAU;AAChB,sBAAM,gBAAA;AACN,+BAAe,QAAQ,IAAI,CAAC,SAAS;AAAA,cACzC;AAAA,cACP,sBACG,oBAAC,QAAA,EAAO,IAAI,EAAC,OAAO,eAAe,QAAQ,eAAe,SAAS,gBAAgB,OAAO,0BACtF,UAAA,oBAACA,WAAA,EAAiB,GACtB,IACA;AAAA,YAAA;AAAA,UAAA,IAER,OAAA,CACR;AAAA,UACC,oCAAiB,KAAA,EAAI,IAAI,EAAC,UAAU,GAAG,MAAM,EAAA,GAAK,UAAA,cAAc,OAAO,EAAA,CAAE,yBACrE,KAAA,EAAI,IAAI,EAAC,UAAU,GAAG,MAAM,EAAA,GACzB,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,qBAAC,YAAA,EAAW,SAAQ,SAAQ,QAAM,MAAC,IAAI,EAAC,YAAY,QAAQ,MAAM,EAAA,GAC7D,UAAA;AAAA,gBAAA,QAAQ,KAAK;AAAA,gBACb,QAAQ,cAAc,KACnB,qBAAC,YAAA,EAAW,WAAU,QAAO,SAAQ,WAAU,OAAM,kBAAiB,IAAI,EAAC,IAAI,OAAM,cAAY,EAAE,eAAe,EAAC,OAAO,QAAQ,YAAA,CAAY,GAAG,UAAA;AAAA,kBAAA;AAAA,kBAC3I,QAAQ;AAAA,kBAAY;AAAA,gBAAA,EAAA,CAC1B;AAAA,cAAA,GAER;AAAA,cACC,QAAQ,kBAAkB,oBAAC,uBAAA,EAAsB,UAAS,WAAU,OAAM,UAAS,cAAY,EAAE,aAAa,EAAA,CAAG;AAAA,cAClH,oBAAC,cAAW,SAAQ,WAAU,OAAM,kBAAiB,IAAI,EAAC,YAAY,GAAG,oBAAoB,eAAA,GACxF,sBAAY,QAAQ,gCAAgB,QAAQ,UAAU,EAAA,CAC3D;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,YAC1E,QAAQ,QAAQ,SAAS,yBACrB,OAAA,EAAM,WAAU,OAAM,SAAS,KAAK,YAAU,MAAC,IAAI,EAAC,UAAU,QAAQ,IAAI,IAAA,GACtE,UAAA,QAAQ,OAAO,IAAI,CAAC,aAAa;AAC9B,oBAAM,QAAQ,MAAM,QAAQ;AAC5B,qBAAO,oBAAC,MAAA,EAAuB,MAAK,SAAQ,SAAQ,YAAW,OAAO,MAAM,OAAO,OAAO,MAAM,MAAA,GAA9E,MAAM,KAA+E;AAAA,YAC3G,CAAC,EAAA,CACL;AAAA,UAAA,EAAA,CAER;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,EAER,GA/DW,QAAQ,EAgEvB;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// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaMessageList con defaultProps y\n// styleOverrides de sus partes: empty, selectedAvatar, item, actions, starButton, itemButton, avatarSlot, avatarButton,\n// avatar, body, text, topRow, unreadDot, sender, threadCount, date, subject, labels.\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';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaMessageList)\nconst THEME_NAME = 'CalcitaMessageList';\nconst MessagesEmpty = piecePart(THEME_NAME, 'Empty', Typography);\nconst MessagesSelectedAvatar = piecePart(THEME_NAME, 'SelectedAvatar', Avatar);\nconst MessagesItem = piecePart(THEME_NAME, 'Item', ListItem);\nconst MessagesActions = piecePart(THEME_NAME, 'Actions', Stack);\nconst MessagesStarButton = piecePart(THEME_NAME, 'StarButton', IconButton);\nconst MessagesItemButton = piecePart(THEME_NAME, 'ItemButton', ListItemButton);\nconst MessagesAvatarSlot = piecePart(THEME_NAME, 'AvatarSlot', ListItemAvatar);\nconst MessagesAvatarButton = piecePart(THEME_NAME, 'AvatarButton', ButtonBase);\nconst MessagesAvatar = piecePart(THEME_NAME, 'Avatar', Avatar);\nconst MessagesBody = piecePart(THEME_NAME, 'Body', Box);\nconst MessagesText = piecePart(THEME_NAME, 'Text', Box);\nconst MessagesTopRow = piecePart(THEME_NAME, 'TopRow', Stack);\nconst MessagesUnreadDot = piecePart(THEME_NAME, 'UnreadDot', Box);\nconst MessagesSender = piecePart(THEME_NAME, 'Sender', Typography);\nconst MessagesThreadCount = piecePart(THEME_NAME, 'ThreadCount', Typography);\nconst MessagesDate = piecePart(THEME_NAME, 'Date', Typography);\nconst MessagesSubject = piecePart(THEME_NAME, 'Subject', Typography);\nconst MessagesLabels = piecePart(THEME_NAME, 'Labels', Stack);\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(props) {\n // lo que el proyecto puso en su tema (components.CalcitaMessageList.defaultProps) y encima lo que llega aquí\n const {\n messages, selectedId = null, onSelect, onToggleStar, selectedIds = [], onSelectionChange, messageActions, renderMessage, emptyText, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('inbox', texts);\n const formatters = useFormatters();\n\n if (messages.length === 0) {\n return <MessagesEmpty variant=\"body2\" color=\"text.secondary\" ownSx={{p: 3, textAlign: 'center'}}>{emptyText ?? t('empty')}</MessagesEmpty>;\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 = <MessagesSelectedAvatar ownSx={{width: AVATAR_PIXELS, height: AVATAR_PIXELS}}>{initialsOf(message.from.name)}</MessagesSelectedAvatar>;\n return (\n <MessagesItem key={message.id} disablePadding secondaryAction={(onToggleStar || messageActions) && (\n <MessagesActions direction=\"row\" ownSx={{alignItems: 'center'}}>\n {onToggleStar && (\n <MessagesStarButton size=\"small\" onClick={() => onToggleStar(message.id)} aria-pressed={Boolean(message.isStarred)}\n aria-label={message.isStarred ? t('unstar') : t('star')} ownSx={{color: message.isStarred ? 'warning.main' : 'text.secondary'}}>\n {message.isStarred ? <StarRoundedIcon fontSize=\"small\" /> : <StarBorderRoundedIcon fontSize=\"small\" />}\n </MessagesStarButton>\n )}\n {messageActions && <ActionsMenu label={t('messageActions', {subject: message.subject})} actions={messageActions(message)} />}\n </MessagesActions>\n )} ownSx={{'& .MuiListItemSecondaryAction-root': {right: 4}}}>\n <MessagesItemButton selected={message.id === selectedId || isChecked} onClick={() => onSelect(message.id)} alignItems=\"flex-start\"\n ownSx={{borderRadius: 2, pr: onToggleStar && messageActions ? 10 : 6}}>\n <MessagesAvatarSlot ownSx={{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 <MessagesAvatarButton role=\"checkbox\" aria-checked={isChecked} aria-label={t('select', {subject: message.subject})}\n ownSx={{borderRadius: '50%'}}\n onClick={(event) => {\n event.stopPropagation();\n toggleSelected(message.id, !isChecked);\n }}>\n {isChecked ? (\n <MessagesAvatar ownSx={{width: AVATAR_PIXELS, height: AVATAR_PIXELS, bgcolor: 'primary.main', color: 'primary.contrastText'}}>\n <CheckRoundedIcon />\n </MessagesAvatar>\n ) : avatar}\n </MessagesAvatarButton>\n ) : avatar}\n </MessagesAvatarSlot>\n {renderMessage ? <MessagesBody ownSx={{minWidth: 0, flex: 1}}>{renderMessage(message)}</MessagesBody> : (\n <MessagesText ownSx={{minWidth: 0, flex: 1}}>\n <MessagesTopRow direction=\"row\" spacing={1} ownSx={{alignItems: 'center'}}>\n {message.isUnread && (\n <MessagesUnreadDot component=\"span\" aria-label={t('unread')} ownSx={{width: UNREAD_DOT_PIXELS, height: UNREAD_DOT_PIXELS, borderRadius: '50%', bgcolor: 'primary.main', flexShrink: 0}} />\n )}\n <MessagesSender variant=\"body2\" noWrap ownSx={{fontWeight: weight, flex: 1}}>\n {message.from.name}\n {message.threadCount > 1 && (\n <MessagesThreadCount component=\"span\" variant=\"caption\" color=\"text.secondary\" ownSx={{ml: 0.5}} aria-label={t('threadCount', {count: message.threadCount})}>\n ({message.threadCount})\n </MessagesThreadCount>\n )}\n </MessagesSender>\n {message.hasAttachments && <AttachFileRoundedIcon fontSize=\"inherit\" color=\"action\" aria-label={t('attachments')} />}\n <MessagesDate variant=\"caption\" color=\"text.secondary\" ownSx={{flexShrink: 0, fontVariantNumeric: 'tabular-nums'}}>\n {inboxDateOf(message.receivedAt, new Date(), formatters)}\n </MessagesDate>\n </MessagesTopRow>\n <MessagesSubject variant=\"body2\" noWrap ownSx={{fontWeight: weight}}>{message.subject}</MessagesSubject>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{message.preview}</Typography>\n {message.labels?.length > 0 && (\n <MessagesLabels direction=\"row\" spacing={0.5} useFlexGap ownSx={{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 </MessagesLabels>\n )}\n </MessagesText>\n )}\n </MessagesItemButton>\n </MessagesItem>\n );\n })}\n </List>\n );\n}\n"],"names":["CheckRoundedIcon"],"mappings":"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"}
|
|
@@ -14,12 +14,27 @@ import { useFormatters } from "../toolkit/useFormatters.js";
|
|
|
14
14
|
import { inboxDateOf } from "./inboxDates.js";
|
|
15
15
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
16
16
|
import { tagOf } from "../ui/labels/tagOf.js";
|
|
17
|
+
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
18
|
+
const THEME_NAME = "CalcitaMessageView";
|
|
19
|
+
const MessageHeaderPart = piecePart(THEME_NAME, "Header", Stack);
|
|
20
|
+
const MessageHeaderText = piecePart(THEME_NAME, "HeaderText", Box);
|
|
21
|
+
const MessageRecipients = piecePart(THEME_NAME, "Recipients", Stack);
|
|
22
|
+
const MessageBodyPart = piecePart(THEME_NAME, "Body", Typography);
|
|
23
|
+
const MessageEmpty = piecePart(THEME_NAME, "Empty", Typography);
|
|
24
|
+
const MessageLabels = piecePart(THEME_NAME, "Labels", Stack);
|
|
25
|
+
const MessageAttachments = piecePart(THEME_NAME, "Attachments", Stack);
|
|
26
|
+
const MessageDivider = piecePart(THEME_NAME, "Divider", Divider);
|
|
27
|
+
const MessageCollapsed = piecePart(THEME_NAME, "Collapsed", ButtonBase);
|
|
28
|
+
const MessageCollapsedAvatar = piecePart(THEME_NAME, "CollapsedAvatar", Avatar);
|
|
29
|
+
const MessageCollapsedText = piecePart(THEME_NAME, "CollapsedText", Box);
|
|
30
|
+
const MessageCollapsedSender = piecePart(THEME_NAME, "CollapsedSender", Typography);
|
|
31
|
+
const MessageCollapsedDate = piecePart(THEME_NAME, "CollapsedDate", Typography);
|
|
17
32
|
const PREVIEW_LENGTH = 90;
|
|
18
33
|
function MessageHeader({ entry, t }) {
|
|
19
34
|
const { formatDateTime } = useFormatters();
|
|
20
|
-
return /* @__PURE__ */ jsxs(
|
|
35
|
+
return /* @__PURE__ */ jsxs(MessageHeaderPart, { direction: "row", spacing: 1.5, ownSx: { alignItems: "center" }, children: [
|
|
21
36
|
/* @__PURE__ */ jsx(Avatar, { children: initialsOf(entry.from.name) }),
|
|
22
|
-
/* @__PURE__ */ jsxs(
|
|
37
|
+
/* @__PURE__ */ jsxs(MessageHeaderText, { ownSx: { minWidth: 0, flex: 1 }, children: [
|
|
23
38
|
/* @__PURE__ */ jsxs(Typography, { variant: "subtitle2", component: "p", noWrap: true, children: [
|
|
24
39
|
entry.from.name,
|
|
25
40
|
" ",
|
|
@@ -38,7 +53,7 @@ function MessageHeader({ entry, t }) {
|
|
|
38
53
|
function MessageBody({ entry, t, onAttachmentClick, renderBody }) {
|
|
39
54
|
return /* @__PURE__ */ jsxs(Stack, { spacing: 2, children: [
|
|
40
55
|
/* @__PURE__ */ jsx(MessageHeader, { entry, t }),
|
|
41
|
-
entry.attachments?.length > 0 && /* @__PURE__ */ jsx(
|
|
56
|
+
entry.attachments?.length > 0 && /* @__PURE__ */ jsx(MessageRecipients, { direction: "row", spacing: 1, useFlexGap: true, ownSx: { flexWrap: "wrap" }, "aria-label": t("attachments"), role: "group", children: entry.attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
42
57
|
Chip,
|
|
43
58
|
{
|
|
44
59
|
icon: /* @__PURE__ */ jsx(AttachFileRoundedIcon, {}),
|
|
@@ -48,15 +63,16 @@ function MessageBody({ entry, t, onAttachmentClick, renderBody }) {
|
|
|
48
63
|
},
|
|
49
64
|
attachment.name
|
|
50
65
|
)) }),
|
|
51
|
-
renderBody ? renderBody(entry) : /* @__PURE__ */ jsx(
|
|
66
|
+
renderBody ? renderBody(entry) : /* @__PURE__ */ jsx(MessageBodyPart, { variant: "body1", component: "div", ownSx: { whiteSpace: "pre-wrap", overflowWrap: "anywhere" }, children: entry.body })
|
|
52
67
|
] });
|
|
53
68
|
}
|
|
54
|
-
function MessageView(
|
|
69
|
+
function MessageView(props) {
|
|
70
|
+
const { message, actions, footer, onAttachmentClick, onExpand, renderBody, texts } = usePieceProps(props, THEME_NAME);
|
|
55
71
|
const { t } = useToolkitTranslation("inbox", texts);
|
|
56
72
|
const formatters = useFormatters();
|
|
57
73
|
const [expanded, setExpanded] = useState({ messageId: null, ids: [] });
|
|
58
74
|
if (!message) {
|
|
59
|
-
return /* @__PURE__ */ jsx(
|
|
75
|
+
return /* @__PURE__ */ jsx(MessageEmpty, { variant: "body2", color: "text.secondary", ownSx: { p: 4, textAlign: "center" }, children: t("noMessageSelected") });
|
|
60
76
|
}
|
|
61
77
|
const expandedIds = expanded.messageId === message.id ? expanded.ids : [];
|
|
62
78
|
const expand = (threadMessageId) => {
|
|
@@ -64,10 +80,10 @@ function MessageView({ message, actions, footer, onAttachmentClick, onExpand, re
|
|
|
64
80
|
onExpand?.(threadMessageId);
|
|
65
81
|
};
|
|
66
82
|
return /* @__PURE__ */ jsxs(Stack, { spacing: 2, component: "article", "aria-label": message.subject, children: [
|
|
67
|
-
actions && /* @__PURE__ */ jsx(
|
|
83
|
+
actions && /* @__PURE__ */ jsx(MessageLabels, { direction: "row", spacing: 0.5, useFlexGap: true, ownSx: { flexWrap: "wrap", alignItems: "center" }, children: actions }),
|
|
68
84
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
69
85
|
/* @__PURE__ */ jsx(Typography, { variant: "h5", component: "h2", children: message.subject }),
|
|
70
|
-
message.labels?.length > 0 && /* @__PURE__ */ jsx(
|
|
86
|
+
message.labels?.length > 0 && /* @__PURE__ */ jsx(MessageAttachments, { direction: "row", spacing: 0.5, useFlexGap: true, ownSx: { flexWrap: "wrap", mt: 1 }, children: message.labels.map((rawLabel) => {
|
|
71
87
|
const label = tagOf(rawLabel);
|
|
72
88
|
return /* @__PURE__ */ jsx(Chip, { size: "small", variant: "outlined", color: label.color, label: label.label }, label.label);
|
|
73
89
|
}) })
|
|
@@ -75,20 +91,20 @@ function MessageView({ message, actions, footer, onAttachmentClick, onExpand, re
|
|
|
75
91
|
message.thread?.length > 0 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: t("threadCount", { count: message.thread.length + 1 }) }),
|
|
76
92
|
message.thread?.map((entry) => expandedIds.includes(entry.id) ? /* @__PURE__ */ jsxs(Box, { children: [
|
|
77
93
|
/* @__PURE__ */ jsx(MessageBody, { entry, t, onAttachmentClick, renderBody }),
|
|
78
|
-
/* @__PURE__ */ jsx(
|
|
94
|
+
/* @__PURE__ */ jsx(MessageDivider, { ownSx: { mt: 2 } })
|
|
79
95
|
] }, entry.id) : /* @__PURE__ */ jsxs(
|
|
80
|
-
|
|
96
|
+
MessageCollapsed,
|
|
81
97
|
{
|
|
82
98
|
onClick: () => expand(entry.id),
|
|
83
99
|
"aria-label": t("expandMessage", { name: entry.from.name, date: inboxDateOf(entry.receivedAt, /* @__PURE__ */ new Date(), formatters) }),
|
|
84
|
-
|
|
100
|
+
ownSx: { justifyContent: "flex-start", textAlign: "left", borderRadius: 2, p: 1, mx: -1, gap: 1.5, "&:hover": { bgcolor: "action.hover" } },
|
|
85
101
|
children: [
|
|
86
|
-
/* @__PURE__ */ jsx(
|
|
87
|
-
/* @__PURE__ */ jsxs(
|
|
88
|
-
/* @__PURE__ */ jsx(
|
|
102
|
+
/* @__PURE__ */ jsx(MessageCollapsedAvatar, { ownSx: { width: 32, height: 32, fontSize: 13 }, children: initialsOf(entry.from.name) }),
|
|
103
|
+
/* @__PURE__ */ jsxs(MessageCollapsedText, { ownSx: { minWidth: 0, flex: 1 }, children: [
|
|
104
|
+
/* @__PURE__ */ jsx(MessageCollapsedSender, { variant: "body2", ownSx: { fontWeight: 600 }, noWrap: true, children: entry.from.name }),
|
|
89
105
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", noWrap: true, children: entry.body.replace(/\s+/g, " ").slice(0, PREVIEW_LENGTH) })
|
|
90
106
|
] }),
|
|
91
|
-
/* @__PURE__ */ jsx(
|
|
107
|
+
/* @__PURE__ */ jsx(MessageCollapsedDate, { variant: "caption", color: "text.secondary", ownSx: { flexShrink: 0 }, children: inboxDateOf(entry.receivedAt, /* @__PURE__ */ new Date(), formatters) })
|
|
92
108
|
]
|
|
93
109
|
},
|
|
94
110
|
entry.id
|
|
@@ -1 +1 @@
|
|
|
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":"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"}
|
|
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// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaMessageView con defaultProps y\n// styleOverrides de sus partes: header, headerText, recipients, body, empty, labels, attachments, divider, collapsed,\n// collapsedAvatar, collapsedText, collapsedSender, collapsedDate.\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';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaMessageView)\nconst THEME_NAME = 'CalcitaMessageView';\nconst MessageHeaderPart = piecePart(THEME_NAME, 'Header', Stack);\nconst MessageHeaderText = piecePart(THEME_NAME, 'HeaderText', Box);\nconst MessageRecipients = piecePart(THEME_NAME, 'Recipients', Stack);\nconst MessageBodyPart = piecePart(THEME_NAME, 'Body', Typography);\nconst MessageEmpty = piecePart(THEME_NAME, 'Empty', Typography);\nconst MessageLabels = piecePart(THEME_NAME, 'Labels', Stack);\nconst MessageAttachments = piecePart(THEME_NAME, 'Attachments', Stack);\nconst MessageDivider = piecePart(THEME_NAME, 'Divider', Divider);\nconst MessageCollapsed = piecePart(THEME_NAME, 'Collapsed', ButtonBase);\nconst MessageCollapsedAvatar = piecePart(THEME_NAME, 'CollapsedAvatar', Avatar);\nconst MessageCollapsedText = piecePart(THEME_NAME, 'CollapsedText', Box);\nconst MessageCollapsedSender = piecePart(THEME_NAME, 'CollapsedSender', Typography);\nconst MessageCollapsedDate = piecePart(THEME_NAME, 'CollapsedDate', Typography);\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 <MessageHeaderPart direction=\"row\" spacing={1.5} ownSx={{alignItems: 'center'}}>\n <Avatar>{initialsOf(entry.from.name)}</Avatar>\n <MessageHeaderText ownSx={{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 </MessageHeaderText>\n </MessageHeaderPart>\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 <MessageRecipients direction=\"row\" spacing={1} useFlexGap ownSx={{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 </MessageRecipients>\n )}\n {renderBody ? renderBody(entry) : (\n <MessageBodyPart variant=\"body1\" component=\"div\" ownSx={{whiteSpace: 'pre-wrap', overflowWrap: 'anywhere'}}>{entry.body}</MessageBodyPart>\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(props) {\n // lo que el proyecto puso en su tema (components.CalcitaMessageView.defaultProps) y encima lo que llega aquí\n const {message, actions, footer, onAttachmentClick, onExpand, renderBody, texts} = usePieceProps(props, THEME_NAME);\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 <MessageEmpty variant=\"body2\" color=\"text.secondary\" ownSx={{p: 4, textAlign: 'center'}}>{t('noMessageSelected')}</MessageEmpty>;\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 && <MessageLabels direction=\"row\" spacing={0.5} useFlexGap ownSx={{flexWrap: 'wrap', alignItems: 'center'}}>{actions}</MessageLabels>}\n <Box>\n <Typography variant=\"h5\" component=\"h2\">{message.subject}</Typography>\n {message.labels?.length > 0 && (\n <MessageAttachments direction=\"row\" spacing={0.5} useFlexGap ownSx={{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 </MessageAttachments>\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 <MessageDivider ownSx={{mt: 2}} />\n </Box>\n ) : (\n <MessageCollapsed key={entry.id} onClick={() => expand(entry.id)} aria-label={t('expandMessage', {name: entry.from.name, date: inboxDateOf(entry.receivedAt, new Date(), formatters)})}\n ownSx={{justifyContent: 'flex-start', textAlign: 'left', borderRadius: 2, p: 1, mx: -1, gap: 1.5, '&:hover': {bgcolor: 'action.hover'}}}>\n <MessageCollapsedAvatar ownSx={{width: 32, height: 32, fontSize: 13}}>{initialsOf(entry.from.name)}</MessageCollapsedAvatar>\n <MessageCollapsedText ownSx={{minWidth: 0, flex: 1}}>\n <MessageCollapsedSender variant=\"body2\" ownSx={{fontWeight: 600}} noWrap>{entry.from.name}</MessageCollapsedSender>\n <Typography variant=\"body2\" color=\"text.secondary\" noWrap>{entry.body.replace(/\\s+/g, ' ').slice(0, PREVIEW_LENGTH)}</Typography>\n </MessageCollapsedText>\n <MessageCollapsedDate variant=\"caption\" color=\"text.secondary\" ownSx={{flexShrink: 0}}>{inboxDateOf(entry.receivedAt, new Date(), formatters)}</MessageCollapsedDate>\n </MessageCollapsed>\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":"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"}
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,8 @@ import { MINIMAL_BACKGROUND, MINIMAL_TONES } from "./theme/components/minimalCom
|
|
|
25
25
|
import { STACKCRAFT, stackcraftDarkPalette, stackcraftLightPalette } from "./theme/stackcraftPalette.js";
|
|
26
26
|
import { toolkitComponents } from "./theme/toolkitComponents.js";
|
|
27
27
|
import { mergeComponents } from "./theme/mergeComponents.js";
|
|
28
|
-
import {
|
|
28
|
+
import { CSS_TOKENS, cssTokenDeclarationsOf, cssTokenOf } from "./theme/cssTokens.js";
|
|
29
|
+
import { FIELD_TONES, GLASS_INSET_PIXELS, GLASS_MENU_RADIUS_PIXELS, GLASS_OPACITY, GLASS_PANEL_RADIUS_PIXELS, accentTextStylesOf, ambientBackgroundOf, canvasFieldStylesOf, fieldPaletteOf, fieldWellStylesOf, fixedBackgroundLayerOf, glassDropOf, glassStylesOf, glassSurfaceOf, isGlassTheme, materialBackgroundOf, materialStylesOf, materialSurfaceOf, scrollEdgeStylesOf } from "./theme/glassSurface.js";
|
|
29
30
|
import { AppLayout, MENU_POSITION } from "./appShell/AppLayout.js";
|
|
30
31
|
import { AuthLayout } from "./appShell/AuthLayout.js";
|
|
31
32
|
import { default as default11 } from "./appShell/BarActions.js";
|
|
@@ -151,6 +152,7 @@ export {
|
|
|
151
152
|
default16 as BarCapsule,
|
|
152
153
|
default13 as BarSearch,
|
|
153
154
|
CHART_VIEW,
|
|
155
|
+
CSS_TOKENS,
|
|
154
156
|
default52 as CalendarHeatmap,
|
|
155
157
|
default18 as Can,
|
|
156
158
|
default55 as CatalogInput,
|
|
@@ -178,6 +180,7 @@ export {
|
|
|
178
180
|
default50 as DownloadMenu,
|
|
179
181
|
ERROR_PAGE_KIND,
|
|
180
182
|
default22 as ErrorPage,
|
|
183
|
+
FIELD_TONES,
|
|
181
184
|
FILE_ACCEPT_PRESETS,
|
|
182
185
|
FILE_REJECTION,
|
|
183
186
|
FONT_SIZE_LEVELS,
|
|
@@ -279,6 +282,8 @@ export {
|
|
|
279
282
|
createHashNavigation,
|
|
280
283
|
createLogger,
|
|
281
284
|
createToolkitTheme,
|
|
285
|
+
cssTokenDeclarationsOf,
|
|
286
|
+
cssTokenOf,
|
|
282
287
|
currentHistoryIndex,
|
|
283
288
|
dayOfPlainDate,
|
|
284
289
|
detectPlatform,
|
|
@@ -287,6 +292,7 @@ export {
|
|
|
287
292
|
downloadsMachine,
|
|
288
293
|
errorsObjectToMessage,
|
|
289
294
|
fieldErrorsOf,
|
|
295
|
+
fieldPaletteOf,
|
|
290
296
|
fieldWellStylesOf,
|
|
291
297
|
fileNameFromContentDisposition,
|
|
292
298
|
fixedBackgroundLayerOf,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -11,24 +11,29 @@ import TextField from "@mui/material/TextField";
|
|
|
11
11
|
import { translateValidationMessage } from "../forms/validationMessages.js";
|
|
12
12
|
import { contrastOnWhite, MIN_CONTRAST, readableVariantOf, HEX_COLOR } from "./colorContrast.js";
|
|
13
13
|
import FieldFrame from "./FieldFrame.js";
|
|
14
|
+
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
15
|
+
const THEME_NAME = "CalcitaColorInput";
|
|
16
|
+
const ColorSwatch = piecePart(THEME_NAME, "Swatch", Box);
|
|
17
|
+
const ColorNativeInput = piecePart(THEME_NAME, "NativeInput", Box);
|
|
14
18
|
const COLOR_LENGTH = 7;
|
|
15
19
|
const NEUTRAL_COLOR = "#000000";
|
|
16
20
|
const RATIO_DECIMALS = 1;
|
|
17
21
|
const SWATCH_PIXELS = 24;
|
|
18
22
|
const SWATCH_EDGE = Object.freeze({ light: "rgba(15, 23, 42, 0.18)", dark: "rgba(255, 255, 255, 0.24)" });
|
|
19
|
-
function ColorInput({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
function ColorInput(props) {
|
|
24
|
+
const {
|
|
25
|
+
name,
|
|
26
|
+
label,
|
|
27
|
+
value,
|
|
28
|
+
onChange,
|
|
29
|
+
onBlur,
|
|
30
|
+
error,
|
|
31
|
+
helperText = " ",
|
|
32
|
+
checkContrast = true,
|
|
33
|
+
disabled = false,
|
|
34
|
+
inputRef,
|
|
35
|
+
texts
|
|
36
|
+
} = usePieceProps(props, THEME_NAME);
|
|
32
37
|
const { t } = useToolkitTranslation("common", texts);
|
|
33
38
|
const generatedId = useId();
|
|
34
39
|
const inputId = `${name ?? "color"}-${generatedId}`;
|
|
@@ -58,10 +63,10 @@ function ColorInput({
|
|
|
58
63
|
slotProps: {
|
|
59
64
|
input: {
|
|
60
65
|
startAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "start", children: /* @__PURE__ */ jsx(
|
|
61
|
-
|
|
66
|
+
ColorSwatch,
|
|
62
67
|
{
|
|
63
68
|
component: "label",
|
|
64
|
-
|
|
69
|
+
ownSx: (theme) => ({
|
|
65
70
|
position: "relative",
|
|
66
71
|
display: "block",
|
|
67
72
|
flexShrink: 0,
|
|
@@ -75,7 +80,7 @@ function ColorInput({
|
|
|
75
80
|
...theme.applyStyles("dark", { boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.dark}` })
|
|
76
81
|
}),
|
|
77
82
|
children: /* @__PURE__ */ jsx(
|
|
78
|
-
|
|
83
|
+
ColorNativeInput,
|
|
79
84
|
{
|
|
80
85
|
component: "input",
|
|
81
86
|
type: "color",
|
|
@@ -83,7 +88,7 @@ function ColorInput({
|
|
|
83
88
|
disabled,
|
|
84
89
|
"aria-label": t("form.pickColor"),
|
|
85
90
|
onChange: (event) => choose(event.target.value),
|
|
86
|
-
|
|
91
|
+
ownSx: { position: "absolute", inset: 0, width: "100%", height: "100%", opacity: 0, border: 0, p: 0, cursor: "inherit" }
|
|
87
92
|
}
|
|
88
93
|
)
|
|
89
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorInput.js","sources":["../../src/inputs/ColorInput.jsx"],"sourcesContent":["// Campo de COLOR suelto (viene de «Identidad de documentos» de PLD): la muestra redonda (el «color well» de Apple, que abre\n// el selector del sistema) dentro del campo del texto #RRGGBB, el contraste contra el blanco mientras se elige y, si el color casi no se leería, el mismo tono más oscuro con su botón\n// para usarlo.\n//\n// Para lo que se imprime o se lee sobre blanco (el color de una marca en un comprobante). El color llega al formulario en\n// mayúsculas (#2B3A67); '' = sin color.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport {HEX_COLOR, MIN_CONTRAST, contrastOnWhite, readableVariantOf} from '@/inputs/colorContrast.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\n\n// #RRGGBB\nconst COLOR_LENGTH = 7;\n// el color que enseña el selector del navegador mientras el texto no es un color\nconst NEUTRAL_COLOR = '#000000';\nconst RATIO_DECIMALS = 1;\n// la muestra redonda (px) y su orilla fina en claro y en oscuro\nconst SWATCH_PIXELS = 24;\nconst SWATCH_EDGE = Object.freeze({light: 'rgba(15, 23, 42, 0.18)', dark: 'rgba(255, 255, 255, 0.24)'});\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value '#RRGGBB' o ''\n * @param {(color: string) => void} props.onChange recibe el color en mayúsculas\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] cuando no hay color\n * @param {boolean} [props.checkContrast] avisa si casi no se lee sobre blanco (por omisión sí)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function ColorInput({\n name, label, value, onChange, onBlur, error, helperText = ' ', checkContrast = true, disabled = false, inputRef, texts,\n})
|
|
1
|
+
{"version":3,"file":"ColorInput.js","sources":["../../src/inputs/ColorInput.jsx"],"sourcesContent":["// Campo de COLOR suelto (viene de «Identidad de documentos» de PLD): la muestra redonda (el «color well» de Apple, que abre\n// el selector del sistema) dentro del campo del texto #RRGGBB, el contraste contra el blanco mientras se elige y, si el color casi no se leería, el mismo tono más oscuro con su botón\n// para usarlo.\n//\n// Para lo que se imprime o se lee sobre blanco (el color de una marca en un comprobante). El color llega al formulario en\n// mayúsculas (#2B3A67); '' = sin color.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaColorInput con defaultProps y\n// styleOverrides de sus partes: swatch, nativeInput.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Alert from '@mui/material/Alert';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport {HEX_COLOR, MIN_CONTRAST, contrastOnWhite, readableVariantOf} from '@/inputs/colorContrast.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaColorInput)\nconst THEME_NAME = 'CalcitaColorInput';\nconst ColorSwatch = piecePart(THEME_NAME, 'Swatch', Box);\nconst ColorNativeInput = piecePart(THEME_NAME, 'NativeInput', Box);\n\n// #RRGGBB\nconst COLOR_LENGTH = 7;\n// el color que enseña el selector del navegador mientras el texto no es un color\nconst NEUTRAL_COLOR = '#000000';\nconst RATIO_DECIMALS = 1;\n// la muestra redonda (px) y su orilla fina en claro y en oscuro\nconst SWATCH_PIXELS = 24;\nconst SWATCH_EDGE = Object.freeze({light: 'rgba(15, 23, 42, 0.18)', dark: 'rgba(255, 255, 255, 0.24)'});\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|null|undefined} props.value '#RRGGBB' o ''\n * @param {(color: string) => void} props.onChange recibe el color en mayúsculas\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] cuando no hay color\n * @param {boolean} [props.checkContrast] avisa si casi no se lee sobre blanco (por omisión sí)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function ColorInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaColorInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, error, helperText = ' ', checkContrast = true, disabled = false, inputRef, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'color'}-${generatedId}`;\n const color = value ?? '';\n const isValidColor = HEX_COLOR.test(color);\n const contrastRatio = isValidColor ? contrastOnWhite(color) : null;\n const suggestion = checkContrast && isValidColor && contrastRatio < MIN_CONTRAST ? readableVariantOf(color) : null;\n const errorText = translateValidationMessage(t, error);\n const contrastText = contrastRatio && checkContrast ? t('form.colorContrast', {ratio: contrastRatio.toFixed(RATIO_DECIMALS)}) : helperText;\n const choose = (nextColor) => onChange(nextColor.toUpperCase());\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <Stack spacing={1}>\n {/* un solo campo: la muestra redonda al inicio (el «color well» de Apple) y el código al lado. Picar la muestra\n abre el selector de colores del sistema (un input type=color invisible encima de ella). Antes eran dos cajas\n sueltas (el selector del navegador en la suya y el código en otra) y se veían como piezas sueltas */}\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n size=\"small\"\n fullWidth\n value={color}\n disabled={disabled}\n placeholder=\"#RRGGBB\"\n onChange={(event) => choose(event.target.value)}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? contrastText}\n slotProps={{\n input: {\n startAdornment: (\n <InputAdornment position=\"start\">\n <ColorSwatch component=\"label\"\n ownSx={(theme) => ({\n position: 'relative', display: 'block', flexShrink: 0, width: SWATCH_PIXELS, height: SWATCH_PIXELS,\n borderRadius: '50%', cursor: disabled ? 'default' : 'pointer',\n backgroundColor: isValidColor ? color : 'transparent',\n // la orilla de la muestra: que se vea aunque el color sea el del fondo (blanco en claro, negro en oscuro)\n boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.light}`,\n ...theme.applyStyles('dark', {boxShadow: `inset 0 0 0 1px ${SWATCH_EDGE.dark}`}),\n })}>\n <ColorNativeInput component=\"input\" type=\"color\" value={isValidColor ? color : NEUTRAL_COLOR} disabled={disabled}\n aria-label={t('form.pickColor')} onChange={(event) => choose(event.target.value)}\n ownSx={{position: 'absolute', inset: 0, width: '100%', height: '100%', opacity: 0, border: 0, p: 0, cursor: 'inherit'}} />\n </ColorSwatch>\n </InputAdornment>\n ),\n },\n htmlInput: {maxLength: COLOR_LENGTH, spellCheck: false, style: {fontVariantNumeric: 'tabular-nums'}},\n }}\n />\n {suggestion && (\n <Alert severity=\"warning\"\n action={<Button color=\"inherit\" size=\"small\" onClick={() => choose(suggestion)}>{t('form.colorUseSuggestion', {color: suggestion})}</Button>}>\n {t('form.colorLowContrast', {suggestion})}\n </Alert>\n )}\n </Stack>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,9 @@ import { useId } from "react";
|
|
|
4
4
|
import Box from "@mui/material/Box";
|
|
5
5
|
import Paper from "@mui/material/Paper";
|
|
6
6
|
import Typography from "@mui/material/Typography";
|
|
7
|
+
import { styled, useThemeProps } from "@mui/material/styles";
|
|
7
8
|
import { FormSectionContext } from "./formLayoutContext.js";
|
|
8
|
-
import {
|
|
9
|
+
import { fieldVariablesOf, accentTextStylesOf, PANEL_RADIUS, fieldWellFromVariablesOf } from "../theme/glassSurface.js";
|
|
9
10
|
const FORM_ROW_CLASS = "calcita-form-row";
|
|
10
11
|
const ROW_MIN_HEIGHT_PIXELS = 52;
|
|
11
12
|
const ROW_PADDING_BLOCK_PIXELS = 8;
|
|
@@ -18,7 +19,8 @@ function rowControlSxOf(theme) {
|
|
|
18
19
|
const roots = [".MuiOutlinedInput-root", ".MuiFilledInput-root", ".MuiPickersOutlinedInput-root"];
|
|
19
20
|
const ringOf = (color, percent) => ({ outline: `${RING.pixels}px solid color-mix(in srgb, ${color} ${percent}%, transparent)`, outlineOffset: 0 });
|
|
20
21
|
return {
|
|
21
|
-
|
|
22
|
+
// los tonos van en variables del renglón (fieldVariablesOf, abajo en FormRow): aquí abajo una regla de oscuro no aplica
|
|
23
|
+
[strong(roots)]: { ...fieldWellFromVariablesOf(theme), outline: "none" },
|
|
22
24
|
[strong(roots.map((root) => `${root}.Mui-focused`))]: ringOf(palette.primary.main, RING.focusPercent),
|
|
23
25
|
[strong(roots.map((root) => `${root}.Mui-error`))]: ringOf(palette.error.main, RING.errorPercent),
|
|
24
26
|
[strong([".MuiOutlinedInput-notchedOutline", ".MuiPickersOutlinedInput-notchedOutline"])]: { display: "none" },
|
|
@@ -32,69 +34,70 @@ function rowControlSxOf(theme) {
|
|
|
32
34
|
"& .MuiFormHelperText-root:has(> .notranslate:only-child)": { display: "none" }
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
37
|
+
const SECTION_THEME_NAME = "CalcitaFormSection";
|
|
38
|
+
const ROW_THEME_NAME = "CalcitaFormRow";
|
|
39
|
+
const FormRowRoot = styled(Box, { name: ROW_THEME_NAME, slot: "Root" })(({ theme }) => {
|
|
40
|
+
const palette = (theme.vars ?? theme).palette;
|
|
41
|
+
return theme.unstable_sx({
|
|
42
|
+
position: "relative",
|
|
43
|
+
display: "flex",
|
|
44
|
+
alignItems: "flex-start",
|
|
45
|
+
gap: 2,
|
|
46
|
+
minHeight: ROW_MIN_HEIGHT_PIXELS,
|
|
47
|
+
px: 2,
|
|
48
|
+
py: `${ROW_PADDING_BLOCK_PIXELS}px`,
|
|
49
|
+
"&:focus-within > label": accentTextStylesOf(theme),
|
|
50
|
+
"&:has(.Mui-error) > label": { color: palette.error.main },
|
|
51
|
+
// los tonos de los campos (los del proyecto con createToolkitTheme fields, o los de Apple) para sus campos
|
|
52
|
+
...fieldVariablesOf(theme),
|
|
53
|
+
...rowControlSxOf(theme)
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
const FormRowLabel = styled(Typography, { name: ROW_THEME_NAME, slot: "Label" })(({ theme }) => theme.unstable_sx({
|
|
57
|
+
flex: `0 0 ${LABEL_COLUMN.share}`,
|
|
58
|
+
maxWidth: LABEL_COLUMN.maxPixels,
|
|
59
|
+
pt: `${LABEL_TOP_PIXELS}px`,
|
|
60
|
+
lineHeight: 1.5,
|
|
61
|
+
color: "text.primary"
|
|
62
|
+
}));
|
|
63
|
+
const FormRowControl = styled(Box, { name: ROW_THEME_NAME, slot: "Control" })({
|
|
64
|
+
flex: 1,
|
|
65
|
+
minWidth: 0,
|
|
66
|
+
display: "flex",
|
|
67
|
+
flexDirection: "column",
|
|
68
|
+
justifyContent: "center",
|
|
69
|
+
minHeight: 40
|
|
70
|
+
});
|
|
71
|
+
const FormSectionRoot = styled(Box, { name: SECTION_THEME_NAME, slot: "Root" })({});
|
|
72
|
+
const FormSectionTitle = styled(Typography, { name: SECTION_THEME_NAME, slot: "Title" })(({ theme }) => theme.unstable_sx({ px: 2, mb: 0.75, fontWeight: 600 }));
|
|
73
|
+
const FormSectionCard = styled(Paper, { name: SECTION_THEME_NAME, slot: "Card" })(({ theme }) => ({
|
|
74
|
+
borderRadius: PANEL_RADIUS,
|
|
75
|
+
overflow: "hidden",
|
|
76
|
+
[`& .${FORM_ROW_CLASS}::before`]: {
|
|
77
|
+
content: '""',
|
|
78
|
+
position: "absolute",
|
|
79
|
+
top: 0,
|
|
80
|
+
right: 0,
|
|
81
|
+
left: theme.spacing(2),
|
|
82
|
+
height: "1px",
|
|
83
|
+
backgroundColor: (theme.vars ?? theme).palette.divider
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
const FormSectionFooter = styled(Typography, { name: SECTION_THEME_NAME, slot: "Footer" })(({ theme }) => theme.unstable_sx({ px: 2, mt: 0.75 }));
|
|
87
|
+
function FormRow(props) {
|
|
88
|
+
const { label, htmlFor, labelId, children } = useThemeProps({ props, name: ROW_THEME_NAME });
|
|
89
|
+
return /* @__PURE__ */ jsxs(FormRowRoot, { className: FORM_ROW_CLASS, children: [
|
|
90
|
+
/* @__PURE__ */ jsx(FormRowLabel, { component: "label", id: labelId, htmlFor, variant: "body1", children: label }),
|
|
91
|
+
/* @__PURE__ */ jsx(FormRowControl, { children })
|
|
92
|
+
] });
|
|
71
93
|
}
|
|
72
|
-
function FormSection(
|
|
94
|
+
function FormSection(props) {
|
|
95
|
+
const { title, footer, children } = useThemeProps({ props, name: SECTION_THEME_NAME });
|
|
73
96
|
const titleId = useId();
|
|
74
|
-
return /* @__PURE__ */ jsx(FormSectionContext, { value: true, children: /* @__PURE__ */ jsxs(
|
|
75
|
-
title && /* @__PURE__ */ jsx(
|
|
76
|
-
/* @__PURE__ */ jsx(
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
variant: "outlined",
|
|
80
|
-
sx: (theme) => ({
|
|
81
|
-
borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`,
|
|
82
|
-
overflow: "hidden",
|
|
83
|
-
// la línea de arriba de cada renglón, desde donde empieza el texto (la del primero la recorta la tarjeta)
|
|
84
|
-
[`& .${FORM_ROW_CLASS}::before`]: {
|
|
85
|
-
content: '""',
|
|
86
|
-
position: "absolute",
|
|
87
|
-
top: 0,
|
|
88
|
-
right: 0,
|
|
89
|
-
left: theme.spacing(2),
|
|
90
|
-
height: "1px",
|
|
91
|
-
backgroundColor: (theme.vars ?? theme).palette.divider
|
|
92
|
-
}
|
|
93
|
-
}),
|
|
94
|
-
children: /* @__PURE__ */ jsx(Box, { sx: { mt: "-1px" }, children })
|
|
95
|
-
}
|
|
96
|
-
),
|
|
97
|
-
footer && /* @__PURE__ */ jsx(Typography, { variant: "caption", component: "p", color: "text.secondary", sx: { px: 2, mt: 0.75 }, children: footer })
|
|
97
|
+
return /* @__PURE__ */ jsx(FormSectionContext, { value: true, children: /* @__PURE__ */ jsxs(FormSectionRoot, { component: "section", "aria-labelledby": title ? titleId : void 0, children: [
|
|
98
|
+
title && /* @__PURE__ */ jsx(FormSectionTitle, { id: titleId, variant: "subtitle2", component: "h2", color: "text.secondary", children: title }),
|
|
99
|
+
/* @__PURE__ */ jsx(FormSectionCard, { variant: "outlined", children: /* @__PURE__ */ jsx(Box, { sx: { mt: "-1px" }, children }) }),
|
|
100
|
+
footer && /* @__PURE__ */ jsx(FormSectionFooter, { variant: "caption", component: "p", color: "text.secondary", children: footer })
|
|
98
101
|
] }) });
|
|
99
102
|
}
|
|
100
103
|
export {
|