@stackcraftmx/calcita 0.28.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 +139 -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 +4 -0
- 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 +64 -64
- 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 +6 -2
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/theme/createToolkitTheme.js +8 -1
- 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 +9 -3
- 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 +1 -0
- 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 +12 -1
- package/dist/types/theme/cssTokens.d.ts +46 -0
- package/dist/types/theme/glassSurface.d.ts +3 -0
- 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":"SelectionActions.js","sources":["../../../src/ui/actions/SelectionActions.jsx"],"sourcesContent":["// Las acciones de lo elegido en una lista («5 pedidos elegidos · Cancelar · Borrar · Quitar selección»), en el lugar que le\n// toca según el equipo. Una sola pieza para que cada lista con casillas (la tabla de servidor, una propia) no repita dónde\n// va cada cosa:\n// - escritorio, y el teléfono sin barra de pestañas abajo (el tema de MUI sin vidrio): un renglón donde se pone la pieza,\n// cada acción como botón con su texto;\n// - teléfono con vidrio, o minimalista con cápsulas (usesMobileTabBar), según phoneLayout (SELECTION_PHONE_LAYOUT):\n// top (por omisión): arriba, en la barra flotante, «5 elegidos ✕» y las acciones como íconos en su propia cápsula,\n// como Apple separa los grupos; las que no caben, al «⋯» (BarActions);\n// en los dos, mientras hay algo elegido, el renglón de arriba queda solo para lo elegido (BarActions exclusive): las\n// demás cápsulas de la pantalla y la lupa se esconden, como el modo de edición del iPhone;\n// split: como iOS en modo de edición (Fotos, Mail, Archivos): arriba «5 elegidos ✕» y abajo, encima de las pestañas,\n// una cápsula fija con las acciones, donde llega el pulgar. Mientras se ve, pide su alto al marco\n// (FLOATING_BOTTOM_SPACE_CSS_VARIABLE) para no tapar el final de la página.\n// Lo pidió el autor: en el teléfono, configurable, y por omisión todo arriba.\n//\n// Con vidrio, la cápsula de arriba deja sus botones del color del texto (BarCapsule: la regla de la barra); abajo, una acción\n// destructiva (color: 'error') conserva su rojo, como pide la guía de Apple para lo que borra.\n//\n// Cada acción puede regresar una promesa: mientras corre, las demás se apagan (nadie cancela dos veces lo mismo).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport GlobalStyles from '@mui/material/GlobalStyles';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport BarActionButton from '@/appShell/BarActionButton.jsx';\nimport BarActions from '@/appShell/BarActions.jsx';\nimport BarCapsule from '@/appShell/BarCapsule.jsx';\nimport {\n FLOATING_BOTTOM_SPACE_CSS_VARIABLE, GLASS_PANEL_GAP_PIXELS, PHONE_MEDIA_QUERY, aboveMobileTabBarOf,\n} from '@/appShell/shellSizes.js';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/** Dónde van las acciones de lo elegido en el teléfono con barra de pestañas abajo: todo arriba o divididas (iOS). */\nexport const SELECTION_PHONE_LAYOUT = Object.freeze({top: 'top', split: 'split'});\n\n// lo que se separa la cápsula de abajo de la barra de pestañas (px), lo mismo que el aviso de versión nueva\nconst FLOATING_GAP_PIXELS = 8;\n// lo que pide al marco mientras flota abajo: su alto (48) y su separación, con aire\nconst FLOATING_BOTTOM_SPACE_PIXELS = 64;\n// lo que solo se ve en el teléfono\nconst PHONE_ONLY_SX = {display: 'none', [PHONE_MEDIA_QUERY]: {display: 'flex'}};\n\n/**\n * ¿En el teléfono lo elegido sale de su renglón (arriba o abajo)? La misma regla que la barra de pestañas de abajo: con ella,\n * el renglón de la pantalla se queda con lo suyo (el conteo de la tabla) y las acciones flotan.\n *\n * @param {object} theme\n * @returns {boolean}\n */\nexport function selectionFloatsOnPhone(theme) {\n return usesMobileTabBar(theme);\n}\n\n/**\n * La cápsula fija abajo, centrada en la ventana, encima de la barra de pestañas.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction bottomCapsuleSxOf(theme) {\n return {\n ...(isGlassTheme(theme) ? glassStylesOf(theme) : minimalFloatingStylesOf(theme)),\n position: 'fixed',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: theme.zIndex.appBar,\n alignItems: 'center',\n gap: 0.5,\n width: 'max-content',\n maxWidth: `calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px)`,\n borderRadius: '999px',\n p: 0.5,\n ...aboveMobileTabBarOf(FLOATING_GAP_PIXELS),\n };\n}\n\n/**\n * @param {object} props\n * @param {string} props.countText «5 pedidos elegidos», ya traducido. La pieza se pinta mientras la pantalla la ponga: con\n * nada elegido, no la ponga\n * @param {Array<{key: string, label: string, icon?: import('react').ReactNode, color?: 'inherit'|'primary'|'secondary'|'error'|'info'|'success'|'warning', disabled?: boolean, onClick: () => unknown}>} [props.actions]\n * label ya traducido; en el teléfono se ve el ícono (sin él, el texto). onClick puede regresar una promesa\n * @param {() => void} props.onClear quitar la selección\n * @param {'top'|'split'} [props.phoneLayout] SELECTION_PHONE_LAYOUT; por omisión todo arriba\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {selection: {clear, actionsLabel, summaryLabel}}\n * @param {object} [props.sx] estilos del renglón\n */\nexport default function SelectionActions({countText, actions = [], onClear, phoneLayout = SELECTION_PHONE_LAYOUT.top, texts, sx}) {\n const theme = useTheme();\n const {t} = useToolkitTranslation('common', texts);\n const [runningActionKey, setRunningActionKey] = useState(null);\n const floatsOnPhone = selectionFloatsOnPhone(theme);\n const extraSx = Array.isArray(sx) ? sx : [sx];\n\n const runAction = async (action) => {\n setRunningActionKey(action.key);\n try {\n await action.onClick();\n } finally {\n setRunningActionKey(null);\n }\n };\n const isDisabledOf = (action) => runningActionKey !== null || Boolean(action.disabled);\n\n const clearButton = (\n <Tooltip title={t('selection.clear')}>\n <IconButton aria-label={t('selection.clear')} onClick={onClear}>\n <CloseRoundedIcon />\n </IconButton>\n </Tooltip>\n );\n\n // el renglón: escritorio siempre; en el teléfono, solo si ahí no flota\n const row = (\n <Stack role=\"toolbar\" aria-label={t('selection.actionsLabel')} direction=\"row\" spacing={1} useFlexGap\n sx={[{flexGrow: 1, alignItems: 'center', flexWrap: 'wrap', minHeight: 32},\n floatsOnPhone && {[PHONE_MEDIA_QUERY]: {display: 'none'}}, ...extraSx]}>\n <Typography variant=\"body2\" sx={{fontWeight: 600, mr: 1}}>{countText}</Typography>\n {actions.map((action) => (\n <Button key={action.key} size=\"small\" color={action.color ?? 'primary'} startIcon={action.icon}\n disabled={isDisabledOf(action)} onClick={() => runAction(action)}>\n {action.label}\n </Button>\n ))}\n <Button size=\"small\" color=\"inherit\" startIcon={<CloseRoundedIcon />} onClick={onClear} sx={{ml: 'auto'}}>\n {t('selection.clear')}\n </Button>\n </Stack>\n );\n\n if (!floatsOnPhone) {\n return row;\n }\n\n const showsBottomCapsule = phoneLayout === SELECTION_PHONE_LAYOUT.split && actions.length > 0;\n\n return (\n <>\n {row}\n {/* arriba: con las demás cápsulas flotantes de la pantalla (BarActions), antes que ellas. Sin label en BarActions:\n el nombre ya lo lleva la cápsula (dos grupos anidados con el mismo nombre se leen dos veces) */}\n <BarActions bare exclusive order={-2} sx={PHONE_ONLY_SX} inlineSx={PHONE_ONLY_SX}>\n <BarCapsule aria-label={t('selection.summaryLabel')} sx={{pl: 2}}>\n <Typography variant=\"body2\" role=\"status\" aria-live=\"polite\" sx={{fontWeight: 600, whiteSpace: 'nowrap', mr: 0.5}}>\n {countText}\n </Typography>\n {clearButton}\n </BarCapsule>\n </BarActions>\n {phoneLayout === SELECTION_PHONE_LAYOUT.top && actions.length > 0 && (\n <BarActions label={t('selection.actionsLabel')} exclusive order={-1} sx={PHONE_ONLY_SX} inlineSx={PHONE_ONLY_SX}>\n {actions.map((action) => (\n <BarActionButton key={action.key} label={action.label} icon={action.icon} color={action.color}\n disabled={isDisabledOf(action)} onClick={() => runAction(action)} />\n ))}\n </BarActions>\n )}\n {showsBottomCapsule && (\n <>\n <GlobalStyles styles={{':root': {[FLOATING_BOTTOM_SPACE_CSS_VARIABLE]: `${FLOATING_BOTTOM_SPACE_PIXELS}px`}}} />\n <Box role=\"toolbar\" aria-label={t('selection.actionsLabel')} sx={[bottomCapsuleSxOf, PHONE_ONLY_SX]}>\n {actions.map((action) => (action.icon ? (\n <Tooltip key={action.key} title={action.label}>\n {/* el span deja ver el tooltip aunque el botón esté apagado */}\n <span>\n <IconButton aria-label={action.label} color={action.color ?? 'default'} disabled={isDisabledOf(action)}\n onClick={() => runAction(action)}>\n {action.icon}\n </IconButton>\n </span>\n </Tooltip>\n ) : (\n <Button key={action.key} size=\"small\" color={action.color ?? 'primary'} disabled={isDisabledOf(action)}\n onClick={() => runAction(action)}>\n {action.label}\n </Button>\n )))}\n </Box>\n </>\n )}\n </>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SelectionActions.js","sources":["../../../src/ui/actions/SelectionActions.jsx"],"sourcesContent":["// Las acciones de lo elegido en una lista («5 pedidos elegidos · Cancelar · Borrar · Quitar selección»), en el lugar que le\n// toca según el equipo. Una sola pieza para que cada lista con casillas (la tabla de servidor, una propia) no repita dónde\n// va cada cosa:\n// - escritorio, y el teléfono sin barra de pestañas abajo (el tema de MUI sin vidrio): un renglón donde se pone la pieza,\n// cada acción como botón con su texto;\n// - teléfono con vidrio, o minimalista con cápsulas (usesMobileTabBar), según phoneLayout (SELECTION_PHONE_LAYOUT):\n// top (por omisión): arriba, en la barra flotante, «5 elegidos ✕» y las acciones como íconos en su propia cápsula,\n// como Apple separa los grupos; las que no caben, al «⋯» (BarActions);\n// en los dos, mientras hay algo elegido, el renglón de arriba queda solo para lo elegido (BarActions exclusive): las\n// demás cápsulas de la pantalla y la lupa se esconden, como el modo de edición del iPhone;\n// split: como iOS en modo de edición (Fotos, Mail, Archivos): arriba «5 elegidos ✕» y abajo, encima de las pestañas,\n// una cápsula fija con las acciones, donde llega el pulgar. Mientras se ve, pide su alto al marco\n// (FLOATING_BOTTOM_SPACE_CSS_VARIABLE) para no tapar el final de la página.\n// Lo pidió el autor: en el teléfono, configurable, y por omisión todo arriba.\n//\n// Con vidrio, la cápsula de arriba deja sus botones del color del texto (BarCapsule: la regla de la barra); abajo, una acción\n// destructiva (color: 'error') conserva su rojo, como pide la guía de Apple para lo que borra.\n//\n// Cada acción puede regresar una promesa: mientras corre, las demás se apagan (nadie cancela dos veces lo mismo).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSelectionActions con defaultProps y\n// styleOverrides de sus partes: bar, count, clear, phoneCount, phoneActions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport GlobalStyles from '@mui/material/GlobalStyles';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport BarActionButton from '@/appShell/BarActionButton.jsx';\nimport BarActions from '@/appShell/BarActions.jsx';\nimport BarCapsule from '@/appShell/BarCapsule.jsx';\nimport {\n FLOATING_BOTTOM_SPACE_CSS_VARIABLE, GLASS_PANEL_GAP_PIXELS, PHONE_MEDIA_QUERY, aboveMobileTabBarOf,\n} from '@/appShell/shellSizes.js';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaSelectionActions)\nconst THEME_NAME = 'CalcitaSelectionActions';\nconst SelectionBar = piecePart(THEME_NAME, 'Bar', Stack);\nconst SelectionCount = piecePart(THEME_NAME, 'Count', Typography);\nconst SelectionClear = piecePart(THEME_NAME, 'Clear', Button);\nconst SelectionPhoneCount = piecePart(THEME_NAME, 'PhoneCount', Typography);\nconst SelectionPhoneActions = piecePart(THEME_NAME, 'PhoneActions', Box);\n\n/** Dónde van las acciones de lo elegido en el teléfono con barra de pestañas abajo: todo arriba o divididas (iOS). */\nexport const SELECTION_PHONE_LAYOUT = Object.freeze({top: 'top', split: 'split'});\n\n// lo que se separa la cápsula de abajo de la barra de pestañas (px), lo mismo que el aviso de versión nueva\nconst FLOATING_GAP_PIXELS = 8;\n// lo que pide al marco mientras flota abajo: su alto (48) y su separación, con aire\nconst FLOATING_BOTTOM_SPACE_PIXELS = 64;\n// lo que solo se ve en el teléfono\nconst PHONE_ONLY_SX = {display: 'none', [PHONE_MEDIA_QUERY]: {display: 'flex'}};\n\n/**\n * ¿En el teléfono lo elegido sale de su renglón (arriba o abajo)? La misma regla que la barra de pestañas de abajo: con ella,\n * el renglón de la pantalla se queda con lo suyo (el conteo de la tabla) y las acciones flotan.\n *\n * @param {object} theme\n * @returns {boolean}\n */\nexport function selectionFloatsOnPhone(theme) {\n return usesMobileTabBar(theme);\n}\n\n/**\n * La cápsula fija abajo, centrada en la ventana, encima de la barra de pestañas.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction bottomCapsuleSxOf(theme) {\n return {\n ...(isGlassTheme(theme) ? glassStylesOf(theme) : minimalFloatingStylesOf(theme)),\n position: 'fixed',\n left: '50%',\n transform: 'translateX(-50%)',\n zIndex: theme.zIndex.appBar,\n alignItems: 'center',\n gap: 0.5,\n width: 'max-content',\n maxWidth: `calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px)`,\n borderRadius: '999px',\n p: 0.5,\n ...aboveMobileTabBarOf(FLOATING_GAP_PIXELS),\n };\n}\n\n/**\n * @param {object} props\n * @param {string} props.countText «5 pedidos elegidos», ya traducido. La pieza se pinta mientras la pantalla la ponga: con\n * nada elegido, no la ponga\n * @param {Array<{key: string, label: string, icon?: import('react').ReactNode, color?: 'inherit'|'primary'|'secondary'|'error'|'info'|'success'|'warning', disabled?: boolean, onClick: () => unknown}>} [props.actions]\n * label ya traducido; en el teléfono se ve el ícono (sin él, el texto). onClick puede regresar una promesa\n * @param {() => void} props.onClear quitar la selección\n * @param {'top'|'split'} [props.phoneLayout] SELECTION_PHONE_LAYOUT; por omisión todo arriba\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {selection: {clear, actionsLabel, summaryLabel}}\n * @param {object} [props.sx] estilos del renglón\n */\nexport default function SelectionActions(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSelectionActions.defaultProps) y encima lo que llega aquí\n const {countText, actions = [], onClear, phoneLayout = SELECTION_PHONE_LAYOUT.top, texts, sx} = usePieceProps(props, THEME_NAME);\n const theme = useTheme();\n const {t} = useToolkitTranslation('common', texts);\n const [runningActionKey, setRunningActionKey] = useState(null);\n const floatsOnPhone = selectionFloatsOnPhone(theme);\n const extraSx = Array.isArray(sx) ? sx : [sx];\n\n const runAction = async (action) => {\n setRunningActionKey(action.key);\n try {\n await action.onClick();\n } finally {\n setRunningActionKey(null);\n }\n };\n const isDisabledOf = (action) => runningActionKey !== null || Boolean(action.disabled);\n\n const clearButton = (\n <Tooltip title={t('selection.clear')}>\n <IconButton aria-label={t('selection.clear')} onClick={onClear}>\n <CloseRoundedIcon />\n </IconButton>\n </Tooltip>\n );\n\n // el renglón: escritorio siempre; en el teléfono, solo si ahí no flota\n const row = (\n <SelectionBar role=\"toolbar\" aria-label={t('selection.actionsLabel')} direction=\"row\" spacing={1} useFlexGap\n ownSx={[{flexGrow: 1, alignItems: 'center', flexWrap: 'wrap', minHeight: 32},\n floatsOnPhone && {[PHONE_MEDIA_QUERY]: {display: 'none'}}, ...extraSx]}>\n <SelectionCount variant=\"body2\" ownSx={{fontWeight: 600, mr: 1}}>{countText}</SelectionCount>\n {actions.map((action) => (\n <Button key={action.key} size=\"small\" color={action.color ?? 'primary'} startIcon={action.icon}\n disabled={isDisabledOf(action)} onClick={() => runAction(action)}>\n {action.label}\n </Button>\n ))}\n <SelectionClear size=\"small\" color=\"inherit\" startIcon={<CloseRoundedIcon />} onClick={onClear} ownSx={{ml: 'auto'}}>\n {t('selection.clear')}\n </SelectionClear>\n </SelectionBar>\n );\n\n if (!floatsOnPhone) {\n return row;\n }\n\n const showsBottomCapsule = phoneLayout === SELECTION_PHONE_LAYOUT.split && actions.length > 0;\n\n return (\n <>\n {row}\n {/* arriba: con las demás cápsulas flotantes de la pantalla (BarActions), antes que ellas. Sin label en BarActions:\n el nombre ya lo lleva la cápsula (dos grupos anidados con el mismo nombre se leen dos veces) */}\n <BarActions bare exclusive order={-2} sx={PHONE_ONLY_SX} inlineSx={PHONE_ONLY_SX}>\n <BarCapsule aria-label={t('selection.summaryLabel')} sx={{pl: 2}}>\n <SelectionPhoneCount variant=\"body2\" role=\"status\" aria-live=\"polite\" ownSx={{fontWeight: 600, whiteSpace: 'nowrap', mr: 0.5}}>\n {countText}\n </SelectionPhoneCount>\n {clearButton}\n </BarCapsule>\n </BarActions>\n {phoneLayout === SELECTION_PHONE_LAYOUT.top && actions.length > 0 && (\n <BarActions label={t('selection.actionsLabel')} exclusive order={-1} sx={PHONE_ONLY_SX} inlineSx={PHONE_ONLY_SX}>\n {actions.map((action) => (\n <BarActionButton key={action.key} label={action.label} icon={action.icon} color={action.color}\n disabled={isDisabledOf(action)} onClick={() => runAction(action)} />\n ))}\n </BarActions>\n )}\n {showsBottomCapsule && (\n <>\n <GlobalStyles styles={{':root': {[FLOATING_BOTTOM_SPACE_CSS_VARIABLE]: `${FLOATING_BOTTOM_SPACE_PIXELS}px`}}} />\n <SelectionPhoneActions role=\"toolbar\" aria-label={t('selection.actionsLabel')} ownSx={[bottomCapsuleSxOf, PHONE_ONLY_SX]}>\n {actions.map((action) => (action.icon ? (\n <Tooltip key={action.key} title={action.label}>\n {/* el span deja ver el tooltip aunque el botón esté apagado */}\n <span>\n <IconButton aria-label={action.label} color={action.color ?? 'default'} disabled={isDisabledOf(action)}\n onClick={() => runAction(action)}>\n {action.icon}\n </IconButton>\n </span>\n </Tooltip>\n ) : (\n <Button key={action.key} size=\"small\" color={action.color ?? 'primary'} disabled={isDisabledOf(action)}\n onClick={() => runAction(action)}>\n {action.label}\n </Button>\n )))}\n </SelectionPhoneActions>\n </>\n )}\n </>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,13 @@ import TextField from "@mui/material/TextField";
|
|
|
10
10
|
import Typography from "@mui/material/Typography";
|
|
11
11
|
import DateRangeOutlinedIcon from "@mui/icons-material/DateRangeOutlined";
|
|
12
12
|
import { useFormatters } from "../../toolkit/useFormatters.js";
|
|
13
|
-
|
|
13
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
14
|
+
const THEME_NAME = "CalcitaDateRangeFilter";
|
|
15
|
+
const RangeChip = piecePart(THEME_NAME, "Chip", Chip);
|
|
16
|
+
const RangePanel = piecePart(THEME_NAME, "Panel", Stack);
|
|
17
|
+
const RangeActions = piecePart(THEME_NAME, "Actions", Stack);
|
|
18
|
+
function DateRangeFilter(props) {
|
|
19
|
+
const { from = "", to = "", onChange, label, texts } = usePieceProps(props, THEME_NAME);
|
|
14
20
|
const { t } = useToolkitTranslation("table", texts);
|
|
15
21
|
const { formatDate } = useFormatters();
|
|
16
22
|
const [anchorElement, setAnchorElement] = useState(null);
|
|
@@ -33,7 +39,7 @@ function DateRangeFilter({ from = "", to = "", onChange, label, texts }) {
|
|
|
33
39
|
const summary = isActive ? t("dateRange.summary", { from: from ? formatDate(from) : "…", to: to ? formatDate(to) : t("dateRange.today") }) : label ?? t("dateRange.label");
|
|
34
40
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
35
41
|
/* @__PURE__ */ jsx(
|
|
36
|
-
|
|
42
|
+
RangeChip,
|
|
37
43
|
{
|
|
38
44
|
icon: /* @__PURE__ */ jsx(DateRangeOutlinedIcon, {}),
|
|
39
45
|
label: summary,
|
|
@@ -42,10 +48,10 @@ function DateRangeFilter({ from = "", to = "", onChange, label, texts }) {
|
|
|
42
48
|
variant: isActive ? "filled" : "outlined",
|
|
43
49
|
color: isActive ? "primary" : "default",
|
|
44
50
|
onDelete: isActive ? clear : void 0,
|
|
45
|
-
|
|
51
|
+
ownSx: { flex: "0 0 auto", height: 32 }
|
|
46
52
|
}
|
|
47
53
|
),
|
|
48
|
-
/* @__PURE__ */ jsx(Popover, { open: Boolean(anchorElement), anchorEl: anchorElement, onClose: close, anchorOrigin: { vertical: "bottom", horizontal: "left" }, children: /* @__PURE__ */ jsxs(
|
|
54
|
+
/* @__PURE__ */ jsx(Popover, { open: Boolean(anchorElement), anchorEl: anchorElement, onClose: close, anchorOrigin: { vertical: "bottom", horizontal: "left" }, children: /* @__PURE__ */ jsxs(RangePanel, { spacing: 2, ownSx: { p: 2, width: 280 }, children: [
|
|
49
55
|
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: t("dateRange.title") }),
|
|
50
56
|
/* @__PURE__ */ jsx(
|
|
51
57
|
TextField,
|
|
@@ -74,7 +80,7 @@ function DateRangeFilter({ from = "", to = "", onChange, label, texts }) {
|
|
|
74
80
|
slotProps: { inputLabel: { shrink: true }, htmlInput: { min: draft.from || void 0 } }
|
|
75
81
|
}
|
|
76
82
|
),
|
|
77
|
-
/* @__PURE__ */ jsxs(
|
|
83
|
+
/* @__PURE__ */ jsxs(RangeActions, { direction: "row", spacing: 1, ownSx: { justifyContent: "flex-end" }, children: [
|
|
78
84
|
isActive && /* @__PURE__ */ jsx(Button, { onClick: clear, children: t("dateRange.clear") }),
|
|
79
85
|
/* @__PURE__ */ jsx(Button, { variant: "contained", onClick: apply, disabled: isInvalid || !(draft.from || draft.to), children: t("dateRange.apply") })
|
|
80
86
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateRangeFilter.js","sources":["../../../src/ui/filters/DateRangeFilter.jsx"],"sourcesContent":["// Un rango de fechas personalizado, como chip al lado de los accesos rápidos.\n//\n// Viaja como AAAA-MM-DD (día, no instante). Convertirlo a \"inicio del día\" y \"fin del día\" lo hace el SERVIDOR con la\n// zona del negocio: si lo hiciera el navegador con la suya, alguien en otra zona pediría otro rango sin saberlo.\n//\n// El calendario es el <input type=\"date\"> del TextField de MUI y no el DatePicker de @mui/x-date-pickers con moment,\n// para no sumar dos dependencias (x-date-pickers y moment) que ninguna\n// pantalla del 22-sep necesita. Si el calendario nativo estorba (Safari de escritorio), se cambia SOLO este archivo.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Popover from '@mui/material/Popover';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\nimport DateRangeOutlinedIcon from '@mui/icons-material/DateRangeOutlined';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\n\n/**\n * @param {object} props\n * @param {string} [props.from] AAAA-MM-DD o '' si no hay\n * @param {string} [props.to] AAAA-MM-DD o '' si no hay\n * @param {(range: {from: string, to: string}) => void} props.onChange '' para quitar\n * @param {string} [props.label] ya traducido; por defecto \"Personalizado\"\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function DateRangeFilter({from = '', to = '', onChange, label, texts})
|
|
1
|
+
{"version":3,"file":"DateRangeFilter.js","sources":["../../../src/ui/filters/DateRangeFilter.jsx"],"sourcesContent":["// Un rango de fechas personalizado, como chip al lado de los accesos rápidos.\n//\n// Viaja como AAAA-MM-DD (día, no instante). Convertirlo a \"inicio del día\" y \"fin del día\" lo hace el SERVIDOR con la\n// zona del negocio: si lo hiciera el navegador con la suya, alguien en otra zona pediría otro rango sin saberlo.\n//\n// El calendario es el <input type=\"date\"> del TextField de MUI y no el DatePicker de @mui/x-date-pickers con moment,\n// para no sumar dos dependencias (x-date-pickers y moment) que ninguna\n// pantalla del 22-sep necesita. Si el calendario nativo estorba (Safari de escritorio), se cambia SOLO este archivo.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaDateRangeFilter con defaultProps y\n// styleOverrides de sus partes: chip, panel, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport Popover from '@mui/material/Popover';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\nimport DateRangeOutlinedIcon from '@mui/icons-material/DateRangeOutlined';\n\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaDateRangeFilter)\nconst THEME_NAME = 'CalcitaDateRangeFilter';\nconst RangeChip = piecePart(THEME_NAME, 'Chip', Chip);\nconst RangePanel = piecePart(THEME_NAME, 'Panel', Stack);\nconst RangeActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * @param {object} props\n * @param {string} [props.from] AAAA-MM-DD o '' si no hay\n * @param {string} [props.to] AAAA-MM-DD o '' si no hay\n * @param {(range: {from: string, to: string}) => void} props.onChange '' para quitar\n * @param {string} [props.label] ya traducido; por defecto \"Personalizado\"\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function DateRangeFilter(props) {\n // lo que el proyecto puso en su tema (components.CalcitaDateRangeFilter.defaultProps) y encima lo que llega aquí\n const {from = '', to = '', onChange, label, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('table', texts);\n const {formatDate} = useFormatters();\n const [anchorElement, setAnchorElement] = useState(null);\n const [draft, setDraft] = useState({from, to});\n const isActive = Boolean(from || to);\n // AAAA-MM-DD se compara bien como texto\n const isInvalid = Boolean(draft.from && draft.to && draft.from > draft.to);\n\n const open = (event) => {\n setDraft({from, to});\n setAnchorElement(event.currentTarget);\n };\n const close = () => setAnchorElement(null);\n const apply = () => {\n onChange(draft);\n close();\n };\n const clear = () => {\n onChange({from: '', to: ''});\n close();\n };\n\n const summary = isActive\n ? t('dateRange.summary', {from: from ? formatDate(from) : '…', to: to ? formatDate(to) : t('dateRange.today')})\n : (label ?? t('dateRange.label'));\n\n return (\n <>\n <RangeChip icon={<DateRangeOutlinedIcon />} label={summary} clickable onClick={open}\n variant={isActive ? 'filled' : 'outlined'} color={isActive ? 'primary' : 'default'}\n onDelete={isActive ? clear : undefined} ownSx={{flex: '0 0 auto', height: 32}} />\n <Popover open={Boolean(anchorElement)} anchorEl={anchorElement} onClose={close} anchorOrigin={{vertical: 'bottom', horizontal: 'left'}}>\n <RangePanel spacing={2} ownSx={{p: 2, width: 280}}>\n <Typography variant=\"subtitle2\">{t('dateRange.title')}</Typography>\n <TextField type=\"date\" size=\"small\" fullWidth label={t('dateRange.from')} value={draft.from}\n helperText={t('dateRange.fromHelp')}\n onChange={(event) => setDraft((currentDraft) => ({...currentDraft, from: event.target.value}))}\n slotProps={{inputLabel: {shrink: true}, htmlInput: {max: draft.to || undefined}}} />\n <TextField type=\"date\" size=\"small\" fullWidth label={t('dateRange.to')} value={draft.to} error={isInvalid}\n helperText={isInvalid ? t('dateRange.invalid') : t('dateRange.toHelp')}\n onChange={(event) => setDraft((currentDraft) => ({...currentDraft, to: event.target.value}))}\n slotProps={{inputLabel: {shrink: true}, htmlInput: {min: draft.from || undefined}}} />\n <RangeActions direction=\"row\" spacing={1} ownSx={{justifyContent: 'flex-end'}}>\n {isActive && <Button onClick={clear}>{t('dateRange.clear')}</Button>}\n <Button variant=\"contained\" onClick={apply} disabled={isInvalid || !(draft.from || draft.to)}>\n {t('dateRange.apply')}\n </Button>\n </RangeActions>\n </RangePanel>\n </Popover>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,YAAY,UAAU,YAAY,QAAQ,IAAI;AACpD,MAAM,aAAa,UAAU,YAAY,SAAS,KAAK;AACvD,MAAM,eAAe,UAAU,YAAY,WAAW,KAAK;AAU3D,SAAwB,gBAAgB,OAAO;AAE3C,QAAM,EAAC,OAAO,IAAI,KAAK,IAAI,UAAU,OAAO,MAAA,IAAS,cAAc,OAAO,UAAU;AACpF,QAAM,EAAC,EAAA,IAAK,sBAAsB,SAAS,KAAK;AAChD,QAAM,EAAC,WAAA,IAAc,cAAA;AACrB,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AACvD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAC,MAAM,IAAG;AAC7C,QAAM,WAAW,QAAQ,QAAQ,EAAE;AAEnC,QAAM,YAAY,QAAQ,MAAM,QAAQ,MAAM,MAAM,MAAM,OAAO,MAAM,EAAE;AAEzE,QAAM,OAAO,CAAC,UAAU;AACpB,aAAS,EAAC,MAAM,IAAG;AACnB,qBAAiB,MAAM,aAAa;AAAA,EACxC;AACA,QAAM,QAAQ,MAAM,iBAAiB,IAAI;AACzC,QAAM,QAAQ,MAAM;AAChB,aAAS,KAAK;AACd,UAAA;AAAA,EACJ;AACA,QAAM,QAAQ,MAAM;AAChB,aAAS,EAAC,MAAM,IAAI,IAAI,IAAG;AAC3B,UAAA;AAAA,EACJ;AAEA,QAAM,UAAU,WACV,EAAE,qBAAqB,EAAC,MAAM,OAAO,WAAW,IAAI,IAAI,KAAK,IAAI,KAAK,WAAW,EAAE,IAAI,EAAE,iBAAiB,GAAE,IAC3G,SAAS,EAAE,iBAAiB;AAEnC,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAU,0BAAO,uBAAA,EAAsB;AAAA,QAAI,OAAO;AAAA,QAAS,WAAS;AAAA,QAAC,SAAS;AAAA,QACzE,SAAS,WAAW,WAAW;AAAA,QAAY,OAAO,WAAW,YAAY;AAAA,QACzE,UAAU,WAAW,QAAQ;AAAA,QAAW,OAAO,EAAC,MAAM,YAAY,QAAQ,GAAA;AAAA,MAAE;AAAA,IAAA;AAAA,IAClF,oBAAC,SAAA,EAAQ,MAAM,QAAQ,aAAa,GAAG,UAAU,eAAe,SAAS,OAAO,cAAc,EAAC,UAAU,UAAU,YAAY,OAAA,GAC3H,UAAA,qBAAC,YAAA,EAAW,SAAS,GAAG,OAAO,EAAC,GAAG,GAAG,OAAO,IAAA,GACzC,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,aAAa,UAAA,EAAE,iBAAiB,GAAE;AAAA,MACtD;AAAA,QAAC;AAAA,QAAA;AAAA,UAAU,MAAK;AAAA,UAAO,MAAK;AAAA,UAAQ,WAAS;AAAA,UAAC,OAAO,EAAE,gBAAgB;AAAA,UAAG,OAAO,MAAM;AAAA,UAC5E,YAAY,EAAE,oBAAoB;AAAA,UAClC,UAAU,CAAC,UAAU,SAAS,CAAC,kBAAkB,EAAC,GAAG,cAAc,MAAM,MAAM,OAAO,QAAO;AAAA,UAC7F,WAAW,EAAC,YAAY,EAAC,QAAQ,KAAA,GAAO,WAAW,EAAC,KAAK,MAAM,MAAM,SAAS;AAAA,QAAC;AAAA,MAAA;AAAA,MAC1F;AAAA,QAAC;AAAA,QAAA;AAAA,UAAU,MAAK;AAAA,UAAO,MAAK;AAAA,UAAQ,WAAS;AAAA,UAAC,OAAO,EAAE,cAAc;AAAA,UAAG,OAAO,MAAM;AAAA,UAAI,OAAO;AAAA,UACrF,YAAY,YAAY,EAAE,mBAAmB,IAAI,EAAE,kBAAkB;AAAA,UACrE,UAAU,CAAC,UAAU,SAAS,CAAC,kBAAkB,EAAC,GAAG,cAAc,IAAI,MAAM,OAAO,QAAO;AAAA,UAC3F,WAAW,EAAC,YAAY,EAAC,QAAQ,KAAA,GAAO,WAAW,EAAC,KAAK,MAAM,QAAQ,SAAS;AAAA,QAAC;AAAA,MAAA;AAAA,MAC5F,qBAAC,cAAA,EAAa,WAAU,OAAM,SAAS,GAAG,OAAO,EAAC,gBAAgB,WAAA,GAC7D,UAAA;AAAA,QAAA,gCAAa,QAAA,EAAO,SAAS,OAAQ,UAAA,EAAE,iBAAiB,GAAE;AAAA,QAC3D,oBAAC,QAAA,EAAO,SAAQ,aAAY,SAAS,OAAO,UAAU,aAAa,EAAE,MAAM,QAAQ,MAAM,KACpF,UAAA,EAAE,iBAAiB,EAAA,CACxB;AAAA,MAAA,EAAA,CACJ;AAAA,IAAA,EAAA,CACJ,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;"}
|
|
@@ -7,27 +7,33 @@ import MenuItem from "@mui/material/MenuItem";
|
|
|
7
7
|
import Select from "@mui/material/Select";
|
|
8
8
|
import Stack from "@mui/material/Stack";
|
|
9
9
|
import { rangeEndOptionsOf, rangeEndAfterStartChange } from "../../periodRanges.js";
|
|
10
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
11
|
+
const THEME_NAME = "CalcitaPeriodRangeSelector";
|
|
12
|
+
const PeriodField = piecePart(THEME_NAME, "Field", FormControl);
|
|
13
|
+
const PeriodSelectPart = piecePart(THEME_NAME, "Select", Select);
|
|
14
|
+
const PeriodRoot = piecePart(THEME_NAME, "Root", Stack);
|
|
10
15
|
const SELECT_MINIMUM_WIDTH = 210;
|
|
11
16
|
function PeriodSelect({ id, label, value, options, onChange }) {
|
|
12
|
-
return /* @__PURE__ */ jsxs(
|
|
17
|
+
return /* @__PURE__ */ jsxs(PeriodField, { size: "small", ownSx: { minWidth: SELECT_MINIMUM_WIDTH, flex: "0 0 auto" }, children: [
|
|
13
18
|
/* @__PURE__ */ jsx(InputLabel, { id: `${id}-label`, children: label }),
|
|
14
19
|
/* @__PURE__ */ jsx(
|
|
15
|
-
|
|
20
|
+
PeriodSelectPart,
|
|
16
21
|
{
|
|
17
22
|
labelId: `${id}-label`,
|
|
18
23
|
label,
|
|
19
24
|
value: value ?? "",
|
|
20
25
|
onChange: (event) => onChange(event.target.value),
|
|
21
|
-
|
|
26
|
+
ownSx: { bgcolor: "background.paper" },
|
|
22
27
|
renderValue: (selectedValue) => options.find((option) => option.value === selectedValue)?.label ?? "",
|
|
23
28
|
children: options.map((option) => /* @__PURE__ */ jsx(MenuItem, { value: option.value, disabled: option.disabled, children: /* @__PURE__ */ jsx(ListItemText, { primary: option.label, secondary: option.disabled ? option.hint : void 0 }) }, option.value))
|
|
24
29
|
}
|
|
25
30
|
)
|
|
26
31
|
] });
|
|
27
32
|
}
|
|
28
|
-
function PeriodRangeSelector(
|
|
33
|
+
function PeriodRangeSelector(props) {
|
|
34
|
+
const { id, options, from, to, fromLabel, toLabel, onChange } = usePieceProps(props, THEME_NAME);
|
|
29
35
|
const endOptions = rangeEndOptionsOf(options, from);
|
|
30
|
-
return /* @__PURE__ */ jsxs(
|
|
36
|
+
return /* @__PURE__ */ jsxs(PeriodRoot, { direction: "row", spacing: 1.5, useFlexGap: true, ownSx: { alignItems: "center", flexWrap: "wrap" }, children: [
|
|
31
37
|
/* @__PURE__ */ jsx(
|
|
32
38
|
PeriodSelect,
|
|
33
39
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeriodRangeSelector.js","sources":["../../../src/ui/filters/PeriodRangeSelector.jsx"],"sourcesContent":["// Un rango de periodos: «desde» y «hasta», cada uno una lista para elegir (de tal quincena a tal quincena, de tal mes a\n// tal mes). Se usa en lugar de un calendario porque los periodos no son días: solo se ofrecen los que existen, y los que no\n// se pueden elegir salen apagados con su razón.\n//\n// «Hasta» solo ofrece lo que hace un rango válido con «desde»: nada de antes y nada que salte un periodo apagado\n// (periodRanges.js). Al cambiar «desde», si el «hasta» que había ya no vale, se iguala a «desde».\n//\n// No sabe de quincenas ni de productos: recibe las opciones ya ordenadas y con sus textos traducidos.\n//\n// Genérico del paquete: function (regla de la casa).\nimport FormControl from '@mui/material/FormControl';\nimport InputLabel from '@mui/material/InputLabel';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\nimport Select from '@mui/material/Select';\nimport Stack from '@mui/material/Stack';\n\nimport {rangeEndAfterStartChange, rangeEndOptionsOf} from '@/periodRanges.js';\n\n// ancho de cada lista: cabe «septiembre 2026 · Q2» sin cortarse\nconst SELECT_MINIMUM_WIDTH = 210;\n\n/**\n * Una lista del rango.\n *\n * @param {object} props\n * @param {string} props.id\n * @param {string} props.label\n * @param {string} props.value\n * @param {(value: string) => void} props.onChange\n * @param {Array<{value: string, label: string, disabled: boolean, hint?: string}>} props.options\n */\nfunction PeriodSelect({id, label, value, options, onChange}) {\n return (\n <
|
|
1
|
+
{"version":3,"file":"PeriodRangeSelector.js","sources":["../../../src/ui/filters/PeriodRangeSelector.jsx"],"sourcesContent":["// Un rango de periodos: «desde» y «hasta», cada uno una lista para elegir (de tal quincena a tal quincena, de tal mes a\n// tal mes). Se usa en lugar de un calendario porque los periodos no son días: solo se ofrecen los que existen, y los que no\n// se pueden elegir salen apagados con su razón.\n//\n// «Hasta» solo ofrece lo que hace un rango válido con «desde»: nada de antes y nada que salte un periodo apagado\n// (periodRanges.js). Al cambiar «desde», si el «hasta» que había ya no vale, se iguala a «desde».\n//\n// No sabe de quincenas ni de productos: recibe las opciones ya ordenadas y con sus textos traducidos.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPeriodRangeSelector con defaultProps y\n// styleOverrides de sus partes: field, select, root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport FormControl from '@mui/material/FormControl';\nimport InputLabel from '@mui/material/InputLabel';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\nimport Select from '@mui/material/Select';\nimport Stack from '@mui/material/Stack';\n\nimport {rangeEndAfterStartChange, rangeEndOptionsOf} from '@/periodRanges.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaPeriodRangeSelector)\nconst THEME_NAME = 'CalcitaPeriodRangeSelector';\nconst PeriodField = piecePart(THEME_NAME, 'Field', FormControl);\nconst PeriodSelectPart = piecePart(THEME_NAME, 'Select', Select);\nconst PeriodRoot = piecePart(THEME_NAME, 'Root', Stack);\n\n// ancho de cada lista: cabe «septiembre 2026 · Q2» sin cortarse\nconst SELECT_MINIMUM_WIDTH = 210;\n\n/**\n * Una lista del rango.\n *\n * @param {object} props\n * @param {string} props.id\n * @param {string} props.label\n * @param {string} props.value\n * @param {(value: string) => void} props.onChange\n * @param {Array<{value: string, label: string, disabled: boolean, hint?: string}>} props.options\n */\nfunction PeriodSelect({id, label, value, options, onChange}) {\n return (\n <PeriodField size=\"small\" ownSx={{minWidth: SELECT_MINIMUM_WIDTH, flex: '0 0 auto'}}>\n <InputLabel id={`${id}-label`}>{label}</InputLabel>\n <PeriodSelectPart labelId={`${id}-label`} label={label} value={value ?? ''} onChange={(event) => onChange(event.target.value)}\n ownSx={{bgcolor: 'background.paper'}}\n renderValue={(selectedValue) => options.find((option) => option.value === selectedValue)?.label ?? ''}>\n {options.map((option) => (\n <MenuItem key={option.value} value={option.value} disabled={option.disabled}>\n {/* la razón de una opción apagada va debajo de su nombre: «sin validar» se lee sin tooltip */}\n <ListItemText primary={option.label} secondary={option.disabled ? option.hint : undefined} />\n </MenuItem>\n ))}\n </PeriodSelectPart>\n </PeriodField>\n );\n}\n\n/**\n * @param {object} props\n * @param {string} props.id prefijo de los id de las dos listas (accesibilidad)\n * @param {Array<{value: string, label: string, disabled: boolean, hint?: string}>} props.options en orden; ya\n * traducidas\n * @param {string|null} props.from\n * @param {string|null} props.to\n * @param {string} props.fromLabel ya traducido\n * @param {string} props.toLabel ya traducido\n * @param {(range: {from: string, to: string}) => void} props.onChange\n */\nexport default function PeriodRangeSelector(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPeriodRangeSelector.defaultProps) y encima lo que llega aquí\n const {id, options, from, to, fromLabel, toLabel, onChange} = usePieceProps(props, THEME_NAME);\n const endOptions = rangeEndOptionsOf(options, from);\n\n return (\n <PeriodRoot direction=\"row\" spacing={1.5} useFlexGap ownSx={{alignItems: 'center', flexWrap: 'wrap'}}>\n <PeriodSelect id={`${id}-from`} label={fromLabel} value={from} options={options}\n onChange={(nextFrom) => onChange({from: nextFrom, to: rangeEndAfterStartChange(options, nextFrom, to)})} />\n <PeriodSelect id={`${id}-to`} label={toLabel} value={to} options={endOptions}\n onChange={(nextTo) => onChange({from, to: nextTo})} />\n </PeriodRoot>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAwBA,MAAM,aAAa;AACnB,MAAM,cAAc,UAAU,YAAY,SAAS,WAAW;AAC9D,MAAM,mBAAmB,UAAU,YAAY,UAAU,MAAM;AAC/D,MAAM,aAAa,UAAU,YAAY,QAAQ,KAAK;AAGtD,MAAM,uBAAuB;AAY7B,SAAS,aAAa,EAAC,IAAI,OAAO,OAAO,SAAS,YAAW;AACzD,SACI,qBAAC,aAAA,EAAY,MAAK,SAAQ,OAAO,EAAC,UAAU,sBAAsB,MAAM,WAAA,GACpE,UAAA;AAAA,IAAA,oBAAC,YAAA,EAAW,IAAI,GAAG,EAAE,UAAW,UAAA,OAAM;AAAA,IACtC;AAAA,MAAC;AAAA,MAAA;AAAA,QAAiB,SAAS,GAAG,EAAE;AAAA,QAAU;AAAA,QAAc,OAAO,SAAS;AAAA,QAAI,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,QACpH,OAAO,EAAC,SAAS,mBAAA;AAAA,QACjB,aAAa,CAAC,kBAAkB,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,aAAa,GAAG,SAAS;AAAA,QACtG,UAAA,QAAQ,IAAI,CAAC,WACV,oBAAC,UAAA,EAA4B,OAAO,OAAO,OAAO,UAAU,OAAO,UAE/D,UAAA,oBAAC,gBAAa,SAAS,OAAO,OAAO,WAAW,OAAO,WAAW,OAAO,OAAO,OAAA,CAAW,EAAA,GAFhF,OAAO,KAGtB,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EACL,GACJ;AAER;AAaA,SAAwB,oBAAoB,OAAO;AAE/C,QAAM,EAAC,IAAI,SAAS,MAAM,IAAI,WAAW,SAAS,SAAA,IAAY,cAAc,OAAO,UAAU;AAC7F,QAAM,aAAa,kBAAkB,SAAS,IAAI;AAElD,SACI,qBAAC,YAAA,EAAW,WAAU,OAAM,SAAS,KAAK,YAAU,MAAC,OAAO,EAAC,YAAY,UAAU,UAAU,UACzF,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAa,IAAI,GAAG,EAAE;AAAA,QAAS,OAAO;AAAA,QAAW,OAAO;AAAA,QAAM;AAAA,QACjD,UAAU,CAAC,aAAa,SAAS,EAAC,MAAM,UAAU,IAAI,yBAAyB,SAAS,UAAU,EAAE,GAAE;AAAA,MAAA;AAAA,IAAA;AAAA,IACpH;AAAA,MAAC;AAAA,MAAA;AAAA,QAAa,IAAI,GAAG,EAAE;AAAA,QAAO,OAAO;AAAA,QAAS,OAAO;AAAA,QAAI,SAAS;AAAA,QACpD,UAAU,CAAC,WAAW,SAAS,EAAC,MAAM,IAAI,QAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAAG,GACtE;AAER;"}
|
|
@@ -7,6 +7,13 @@ import { alpha } from "@mui/material/styles";
|
|
|
7
7
|
import Chip from "@mui/material/Chip";
|
|
8
8
|
import Typography from "@mui/material/Typography";
|
|
9
9
|
import ScrollableRow from "../layout/ScrollableRow.js";
|
|
10
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
11
|
+
const THEME_NAME = "CalcitaQuickFilters";
|
|
12
|
+
const QuickRoot = piecePart(THEME_NAME, "Root", Box);
|
|
13
|
+
const QuickCaption = piecePart(THEME_NAME, "Caption", Typography);
|
|
14
|
+
const QuickChip = piecePart(THEME_NAME, "Chip", Chip);
|
|
15
|
+
const QuickChipLabel = piecePart(THEME_NAME, "ChipLabel", Box);
|
|
16
|
+
const QuickChipCount = piecePart(THEME_NAME, "ChipCount", Box);
|
|
10
17
|
const ACTIVE_COUNT_VEIL_OPACITY = 0.18;
|
|
11
18
|
function activeCountVeilOf(theme) {
|
|
12
19
|
if (theme.vars) {
|
|
@@ -24,7 +31,8 @@ function compactCount(count) {
|
|
|
24
31
|
}
|
|
25
32
|
return String(count);
|
|
26
33
|
}
|
|
27
|
-
function QuickFilters(
|
|
34
|
+
function QuickFilters(props) {
|
|
35
|
+
const { value, onChange, options = [], caption, ariaLabel, extra, texts } = usePieceProps(props, THEME_NAME);
|
|
28
36
|
const { t } = useToolkitTranslation("table", texts);
|
|
29
37
|
const activeChip = useRef(null);
|
|
30
38
|
const isFirstRender = useRef(true);
|
|
@@ -39,13 +47,13 @@ function QuickFilters({ value, onChange, options = [], caption, ariaLabel, extra
|
|
|
39
47
|
if (options.length <= 1) {
|
|
40
48
|
return null;
|
|
41
49
|
}
|
|
42
|
-
return /* @__PURE__ */ jsxs(
|
|
43
|
-
caption && /* @__PURE__ */ jsx(
|
|
50
|
+
return /* @__PURE__ */ jsxs(QuickRoot, { ownSx: { mb: 1 }, children: [
|
|
51
|
+
caption && /* @__PURE__ */ jsx(QuickCaption, { variant: "caption", color: "text.secondary", ownSx: { px: 0.25 }, children: caption }),
|
|
44
52
|
/* @__PURE__ */ jsxs(ScrollableRow, { role: "tablist", "aria-label": ariaLabel, children: [
|
|
45
53
|
options.map((option) => {
|
|
46
54
|
const isActive = option.value === value;
|
|
47
55
|
return /* @__PURE__ */ jsx(
|
|
48
|
-
|
|
56
|
+
QuickChip,
|
|
49
57
|
{
|
|
50
58
|
ref: isActive ? activeChip : null,
|
|
51
59
|
role: "tab",
|
|
@@ -55,14 +63,14 @@ function QuickFilters({ value, onChange, options = [], caption, ariaLabel, extra
|
|
|
55
63
|
onDelete: isActive && option.value ? () => onChange("") : void 0,
|
|
56
64
|
variant: isActive ? "filled" : "outlined",
|
|
57
65
|
color: isActive ? "primary" : "default",
|
|
58
|
-
label: /* @__PURE__ */ jsxs(
|
|
66
|
+
label: /* @__PURE__ */ jsxs(QuickChipLabel, { component: "span", ownSx: { display: "inline-flex", alignItems: "center", gap: 0.75 }, children: [
|
|
59
67
|
/* @__PURE__ */ jsx("span", { children: option.label }),
|
|
60
68
|
option.count !== null && option.count !== void 0 && /* @__PURE__ */ jsx(
|
|
61
|
-
|
|
69
|
+
QuickChipCount,
|
|
62
70
|
{
|
|
63
71
|
component: "span",
|
|
64
72
|
"aria-label": t("quickFilters.countAriaLabel", { count: option.count }),
|
|
65
|
-
|
|
73
|
+
ownSx: (theme) => ({
|
|
66
74
|
px: 0.75,
|
|
67
75
|
borderRadius: "6px",
|
|
68
76
|
fontSize: 11,
|
|
@@ -76,7 +84,7 @@ function QuickFilters({ value, onChange, options = [], caption, ariaLabel, extra
|
|
|
76
84
|
}
|
|
77
85
|
)
|
|
78
86
|
] }),
|
|
79
|
-
|
|
87
|
+
ownSx: { flex: "0 0 auto", height: 32 }
|
|
80
88
|
},
|
|
81
89
|
option.value || "__all__"
|
|
82
90
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuickFilters.js","sources":["../../../src/ui/filters/QuickFilters.jsx"],"sourcesContent":["// Accesos rápidos: una tira de chips donde se elige UNO (p. ej. el estatus de un pedido).\n//\n// Es el complemento de FilterChips, no lo mismo:\n// - FilterChips dice qué filtros están PUESTOS y sirve para quitarlos;\n// - esto dice qué opciones HAY y sirve para saltar entre ellas de un toque.\n//\n// El conteo de cada chip sale de las facetas del servidor, contadas sobre el conjunto COMPLETO y sin este filtro\n// puesto: al elegir una, las demás siguen diciendo cuántas tienen.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useRef} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport {alpha} from '@mui/material/styles';\nimport Chip from '@mui/material/Chip';\nimport Typography from '@mui/material/Typography';\n\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\n\n\n// qué tan marcado va el velo del contador del filtro elegido\nconst ACTIVE_COUNT_VEIL_OPACITY = 0.18;\n\n/**\n * El velo del contador del filtro elegido: el color del texto base (negro en claro, blanco en oscuro) casi transparente.\n * Un tema con variables CSS trae ese color en theme.vars; el tema por omisión de MUI, en su modo.\n *\n * @param {object} theme tema de MUI\n * @returns {string} color CSS\n */\nfunction activeCountVeilOf(theme) {\n if (theme.vars) {\n return `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${ACTIVE_COUNT_VEIL_OPACITY})`;\n }\n const baseTextColor = theme.palette.mode === 'dark' ? theme.palette.common.white : theme.palette.common.black;\n\n return alpha(baseTextColor, ACTIVE_COUNT_VEIL_OPACITY);\n}\n\n/** \"99+\" arriba de 99 y \"1.2k\" arriba de 999: el chip no se estira. */\nfunction compactCount(count) {\n if (count > 999) {\n return `${(count / 1000).toFixed(1)}k`;\n }\n if (count > 99) {\n return '99+';\n }\n return String(count);\n}\n\n/**\n * @param {object} props\n * @param {string} props.value opción activa ('' = todas)\n * @param {(value: string) => void} props.onChange\n * @param {Array<{value: string, label: import('react').ReactNode, count?: number}>} [props.options]\n * @param {import('react').ReactNode} [props.caption]\n * @param {string} [props.ariaLabel]\n * @param {import('react').ReactNode} [props.extra] al final de la tira (p. ej. un rango de fechas)\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function QuickFilters({value, onChange, options = [], caption, ariaLabel, extra, texts})
|
|
1
|
+
{"version":3,"file":"QuickFilters.js","sources":["../../../src/ui/filters/QuickFilters.jsx"],"sourcesContent":["// Accesos rápidos: una tira de chips donde se elige UNO (p. ej. el estatus de un pedido).\n//\n// Es el complemento de FilterChips, no lo mismo:\n// - FilterChips dice qué filtros están PUESTOS y sirve para quitarlos;\n// - esto dice qué opciones HAY y sirve para saltar entre ellas de un toque.\n//\n// El conteo de cada chip sale de las facetas del servidor, contadas sobre el conjunto COMPLETO y sin este filtro\n// puesto: al elegir una, las demás siguen diciendo cuántas tienen.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaQuickFilters con defaultProps y\n// styleOverrides de sus partes: root, caption, chip, chipLabel, chipCount.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useRef} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport {alpha} from '@mui/material/styles';\nimport Chip from '@mui/material/Chip';\nimport Typography from '@mui/material/Typography';\n\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaQuickFilters)\nconst THEME_NAME = 'CalcitaQuickFilters';\nconst QuickRoot = piecePart(THEME_NAME, 'Root', Box);\nconst QuickCaption = piecePart(THEME_NAME, 'Caption', Typography);\nconst QuickChip = piecePart(THEME_NAME, 'Chip', Chip);\nconst QuickChipLabel = piecePart(THEME_NAME, 'ChipLabel', Box);\nconst QuickChipCount = piecePart(THEME_NAME, 'ChipCount', Box);\n\n\n// qué tan marcado va el velo del contador del filtro elegido\nconst ACTIVE_COUNT_VEIL_OPACITY = 0.18;\n\n/**\n * El velo del contador del filtro elegido: el color del texto base (negro en claro, blanco en oscuro) casi transparente.\n * Un tema con variables CSS trae ese color en theme.vars; el tema por omisión de MUI, en su modo.\n *\n * @param {object} theme tema de MUI\n * @returns {string} color CSS\n */\nfunction activeCountVeilOf(theme) {\n if (theme.vars) {\n return `rgba(${theme.vars.palette.common.onBackgroundChannel} / ${ACTIVE_COUNT_VEIL_OPACITY})`;\n }\n const baseTextColor = theme.palette.mode === 'dark' ? theme.palette.common.white : theme.palette.common.black;\n\n return alpha(baseTextColor, ACTIVE_COUNT_VEIL_OPACITY);\n}\n\n/** \"99+\" arriba de 99 y \"1.2k\" arriba de 999: el chip no se estira. */\nfunction compactCount(count) {\n if (count > 999) {\n return `${(count / 1000).toFixed(1)}k`;\n }\n if (count > 99) {\n return '99+';\n }\n return String(count);\n}\n\n/**\n * @param {object} props\n * @param {string} props.value opción activa ('' = todas)\n * @param {(value: string) => void} props.onChange\n * @param {Array<{value: string, label: import('react').ReactNode, count?: number}>} [props.options]\n * @param {import('react').ReactNode} [props.caption]\n * @param {string} [props.ariaLabel]\n * @param {import('react').ReactNode} [props.extra] al final de la tira (p. ej. un rango de fechas)\n * @param {object} [props.texts] los textos que cambian, con la forma de `table` en los textos del paquete\n */\nexport default function QuickFilters(props) {\n // lo que el proyecto puso en su tema (components.CalcitaQuickFilters.defaultProps) y encima lo que llega aquí\n const {value, onChange, options = [], caption, ariaLabel, extra, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('table', texts);\n const activeChip = useRef(null);\n const isFirstRender = useRef(true);\n\n // al cambiar la selección desde fuera (alguien limpió los filtros) el chip activo se trae a la vista; en el primer\n // render sin animación: una tira que se mueve sola al abrir la pantalla se siente rota\n useEffect(() => {\n if (!activeChip.current) {\n return;\n }\n const behavior = isFirstRender.current ? 'instant' : 'smooth';\n isFirstRender.current = false;\n activeChip.current.scrollIntoView({behavior, block: 'nearest', inline: 'center'});\n }, [value]);\n\n // guard clause: con una sola opción no hay entre qué saltar\n if (options.length <= 1) {\n return null;\n }\n\n return (\n <QuickRoot ownSx={{mb: 1}}>\n {caption && (\n <QuickCaption variant=\"caption\" color=\"text.secondary\" ownSx={{px: 0.25}}>{caption}</QuickCaption>\n )}\n <ScrollableRow role=\"tablist\" aria-label={ariaLabel}>\n {options.map((option) => {\n const isActive = option.value === value;\n return (\n <QuickChip\n key={option.value || '__all__'}\n ref={isActive ? activeChip : null}\n role=\"tab\"\n aria-selected={isActive}\n clickable\n onClick={() => onChange(isActive ? '' : option.value)}\n // el elegido trae su ×: \"volver a Todas\" no se descubre solo\n onDelete={isActive && option.value ? () => onChange('') : undefined}\n variant={isActive ? 'filled' : 'outlined'}\n color={isActive ? 'primary' : 'default'}\n label={(\n <QuickChipLabel component=\"span\" ownSx={{display: 'inline-flex', alignItems: 'center', gap: 0.75}}>\n <span>{option.label}</span>\n {option.count !== null && option.count !== undefined && (\n <QuickChipCount component=\"span\"\n aria-label={t('quickFilters.countAriaLabel', {count: option.count})}\n // velo del color del texto base (negro en claro, blanco en oscuro) y no rgba fijo\n ownSx={(theme) => ({\n px: 0.75, borderRadius: '6px', fontSize: 11, fontWeight: 700, minWidth: 18,\n textAlign: 'center', color: 'inherit',\n bgcolor: isActive ? activeCountVeilOf(theme) : 'action.selected',\n })}>\n {compactCount(option.count)}\n </QuickChipCount>\n )}\n </QuickChipLabel>\n )}\n ownSx={{flex: '0 0 auto', height: 32}}\n />\n );\n })}\n {extra}\n </ScrollableRow>\n </QuickRoot>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import ToggleButton from "@mui/material/ToggleButton";
|
|
4
4
|
import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
|
|
5
|
+
import { styled, useThemeProps } from "@mui/material/styles";
|
|
5
6
|
import { isGlassTheme, glassDropOf } from "../../theme/glassSurface.js";
|
|
6
7
|
import { textSizeOf } from "../../theme/appleTracking.js";
|
|
7
8
|
import ScrollableRow from "../layout/ScrollableRow.js";
|
|
@@ -17,16 +18,20 @@ const SEGMENTED_CONTROL_SX = {
|
|
|
17
18
|
flex: "0 0 auto",
|
|
18
19
|
"& .MuiToggleButton-root": {
|
|
19
20
|
border: 0,
|
|
20
|
-
borderRadius: "999px
|
|
21
|
+
borderRadius: "999px",
|
|
22
|
+
m: 0,
|
|
21
23
|
px: 1.75,
|
|
22
24
|
py: 0.375,
|
|
23
|
-
m: "0 !important",
|
|
24
25
|
...textSizeOf(13),
|
|
25
26
|
fontWeight: 600,
|
|
26
27
|
textTransform: "none",
|
|
27
28
|
color: "text.secondary",
|
|
28
29
|
whiteSpace: "nowrap"
|
|
29
30
|
},
|
|
31
|
+
// la píldora de cada opción. OJO: con dos clases; el grupo de MUI quita las esquinas y junta los botones de en medio con
|
|
32
|
+
// reglas de dos clases (& .MuiToggleButtonGroup-middleButton). Antes se le ganaba con !important, y así el tema del
|
|
33
|
+
// proyecto tampoco podía cambiar la forma
|
|
34
|
+
"& .MuiToggleButton-root.MuiToggleButtonGroup-grouped": { border: 0, borderRadius: "999px", m: 0 },
|
|
30
35
|
"& .MuiToggleButton-root.Mui-selected, & .MuiToggleButton-root.Mui-selected:hover": {
|
|
31
36
|
bgcolor: "primary.main",
|
|
32
37
|
color: "primary.contrastText"
|
|
@@ -55,18 +60,20 @@ function glassSegmentedSxOf(theme) {
|
|
|
55
60
|
"& .MuiToggleButton-root.Mui-selected, & .MuiToggleButton-root.Mui-selected:hover": { ...glassDropOf(theme), color: "text.primary" }
|
|
56
61
|
};
|
|
57
62
|
}
|
|
58
|
-
|
|
63
|
+
const THEME_NAME = "CalcitaSegmentedControl";
|
|
64
|
+
const SegmentedControlRoot = styled(ToggleButtonGroup, { name: THEME_NAME, slot: "Root" })(({ theme }) => theme.unstable_sx([SEGMENTED_CONTROL_SX, glassSegmentedSxOf(theme)]));
|
|
65
|
+
function SegmentedControl(props) {
|
|
66
|
+
const { options, value, onChange, ariaLabel } = useThemeProps({ props, name: THEME_NAME });
|
|
59
67
|
return (
|
|
60
68
|
// la fila deja 4 px arriba y abajo para que la sombra no se corte (ScrollableRow, py 0.5); el margen negativo los
|
|
61
69
|
// devuelve: el control mide sus 40 px y se alinea con los campos de junto (quedaba 4 px más arriba)
|
|
62
70
|
/* @__PURE__ */ jsx(ScrollableRow, { gap: 1, py: ROW_SHADOW_ROOM, sx: { alignItems: "center", my: -ROW_SHADOW_ROOM }, children: /* @__PURE__ */ jsx(
|
|
63
|
-
|
|
71
|
+
SegmentedControlRoot,
|
|
64
72
|
{
|
|
65
73
|
exclusive: true,
|
|
66
74
|
size: "small",
|
|
67
75
|
value,
|
|
68
76
|
"aria-label": ariaLabel,
|
|
69
|
-
sx: [SEGMENTED_CONTROL_SX, glassSegmentedSxOf],
|
|
70
77
|
onChange: (event, nextValue) => nextValue !== null && onChange(nextValue),
|
|
71
78
|
children: options.map((option) => /* @__PURE__ */ jsx(ToggleButton, { value: option.value, disabled: option.disabled, children: option.label }, option.value))
|
|
72
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControl.js","sources":["../../../src/ui/filters/SegmentedControl.jsx"],"sourcesContent":["// Pestañas en píldora: una tira de botones donde se elige UNO (el producto de una lista, la vista de un\n// tablero). Va en un renglón que se desliza en el teléfono, como las pestañas de una app móvil.\n//\n// SEGMENTED_CONTROL_SX es el estilo de la tira; se exporta para quien arma su propia tira con menús adentro (una barra\n// de productos con menús).\n//\n// Con el vidrio del tema es el control segmentado de Liquid Glass: la tira sin borde y lo elegido en la «gota» (la misma\n// de las pestañas y de los botones de opción), no en un relleno de color. Con el naranja fijo le ganaba al vidrio, no se\n// parecía a las pestañas de al lado y en oscuro quedaba con letra negra.\n//\n// Genérico del paquete: function (regla de la casa).\nimport ToggleButton from '@mui/material/ToggleButton';\nimport ToggleButtonGroup from '@mui/material/ToggleButtonGroup';\n\nimport {glassDropOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\n\n// con vidrio, el alto de cada opción: con el margen de la tira (3 px arriba y abajo) da los 40 px de un campo\n// el aire de la fila para la sombra, en unidades de spacing (4 px)\nconst ROW_SHADOW_ROOM = 0.5;\nconst GLASS_SEGMENT_MIN_HEIGHT_PIXELS = 34;\n\nexport const SEGMENTED_CONTROL_SX = {\n gap: 0.5,\n p: 0.375,\n border: 1,\n borderColor: 'divider',\n borderRadius: 999,\n flexWrap: 'nowrap',\n flex: '0 0 auto',\n '& .MuiToggleButton-root': {\n border: 0, borderRadius: '999px
|
|
1
|
+
{"version":3,"file":"SegmentedControl.js","sources":["../../../src/ui/filters/SegmentedControl.jsx"],"sourcesContent":["// Pestañas en píldora: una tira de botones donde se elige UNO (el producto de una lista, la vista de un\n// tablero). Va en un renglón que se desliza en el teléfono, como las pestañas de una app móvil.\n//\n// SEGMENTED_CONTROL_SX es el estilo de la tira; se exporta para quien arma su propia tira con menús adentro (una barra\n// de productos con menús).\n//\n// Con el vidrio del tema es el control segmentado de Liquid Glass: la tira sin borde y lo elegido en la «gota» (la misma\n// de las pestañas y de los botones de opción), no en un relleno de color. Con el naranja fijo le ganaba al vidrio, no se\n// parecía a las pestañas de al lado y en oscuro quedaba con letra negra.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSegmentedControl con styleOverrides.root\n// (la tira y, adentro, sus opciones: '& .MuiToggleButton-root') y defaultProps.\n//\n// Genérico del paquete: function (regla de la casa).\nimport ToggleButton from '@mui/material/ToggleButton';\nimport ToggleButtonGroup from '@mui/material/ToggleButtonGroup';\nimport {styled, useThemeProps} from '@mui/material/styles';\n\nimport {glassDropOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\n\n// con vidrio, el alto de cada opción: con el margen de la tira (3 px arriba y abajo) da los 40 px de un campo\n// el aire de la fila para la sombra, en unidades de spacing (4 px)\nconst ROW_SHADOW_ROOM = 0.5;\nconst GLASS_SEGMENT_MIN_HEIGHT_PIXELS = 34;\n\nexport const SEGMENTED_CONTROL_SX = {\n gap: 0.5,\n p: 0.375,\n border: 1,\n borderColor: 'divider',\n borderRadius: 999,\n flexWrap: 'nowrap',\n flex: '0 0 auto',\n '& .MuiToggleButton-root': {\n border: 0, borderRadius: '999px', m: 0, px: 1.75, py: 0.375,\n ...textSizeOf(13), fontWeight: 600, textTransform: 'none', color: 'text.secondary', whiteSpace: 'nowrap',\n },\n // la píldora de cada opción. OJO: con dos clases; el grupo de MUI quita las esquinas y junta los botones de en medio con\n // reglas de dos clases (& .MuiToggleButtonGroup-middleButton). Antes se le ganaba con !important, y así el tema del\n // proyecto tampoco podía cambiar la forma\n '& .MuiToggleButton-root.MuiToggleButtonGroup-grouped': {border: 0, borderRadius: '999px', m: 0},\n '& .MuiToggleButton-root.Mui-selected, & .MuiToggleButton-root.Mui-selected:hover': {\n bgcolor: 'primary.main', color: 'primary.contrastText',\n },\n};\n\n/**\n * Lo que cambia con vidrio: sin borde (la tira ya se marca sola) y lo elegido en su gota.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction glassSegmentedSxOf(theme) {\n // con el minimalista: la tira gris y lo elegido un escalón arriba, como el de Apple (los pone el tema); sin el naranja lleno\n if (theme.toolkit?.look === 'minimal') {\n return {\n border: 0,\n '& .MuiToggleButton-root': {minHeight: GLASS_SEGMENT_MIN_HEIGHT_PIXELS},\n '& .MuiToggleButton-root.Mui-selected, & .MuiToggleButton-root.Mui-selected:hover': {\n bgcolor: 'background.paper', color: 'text.primary', ...theme.applyStyles('dark', {bgcolor: 'rgba(118, 118, 128, 0.36)'}),\n },\n };\n }\n if (!isGlassTheme(theme)) {\n return {};\n }\n return {\n border: 0,\n // del mismo alto que un campo (40 px con su margen de 3 px arriba y abajo): en una fila de filtros junto a «Desde» y\n // «Hasta» quedaba más bajito y descuadrado (lo reportaron)\n '& .MuiToggleButton-root': {minHeight: GLASS_SEGMENT_MIN_HEIGHT_PIXELS},\n '& .MuiToggleButton-root.Mui-selected, & .MuiToggleButton-root.Mui-selected:hover': {...glassDropOf(theme), color: 'text.primary'},\n };\n}\n\n// el nombre de la pieza en el tema (theme.components.CalcitaSegmentedControl)\nconst THEME_NAME = 'CalcitaSegmentedControl';\n\n// la tira con su estilo y el de vidrio o minimalista; el tema del proyecto (styleOverrides.root) va después y le gana\nconst SegmentedControlRoot = styled(ToggleButtonGroup, {name: THEME_NAME, slot: 'Root'})(({theme}) => (\n theme.unstable_sx([SEGMENTED_CONTROL_SX, glassSegmentedSxOf(theme)])\n));\n\n/**\n * @param {object} props\n * @param {Array<{value: string, label: string, disabled?: boolean}>} props.options en el orden en que se pintan; ya\n * traducidas\n * @param {string|null} props.value\n * @param {(value: string) => void} props.onChange\n * @param {string} props.ariaLabel ya traducido\n */\nexport default function SegmentedControl(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSegmentedControl.defaultProps) y encima lo que llega aquí\n const {options, value, onChange, ariaLabel} = useThemeProps({props, name: THEME_NAME});\n return (\n // la fila deja 4 px arriba y abajo para que la sombra no se corte (ScrollableRow, py 0.5); el margen negativo los\n // devuelve: el control mide sus 40 px y se alinea con los campos de junto (quedaba 4 px más arriba)\n <ScrollableRow gap={1} py={ROW_SHADOW_ROOM} sx={{alignItems: 'center', my: -ROW_SHADOW_ROOM}}>\n <SegmentedControlRoot exclusive size=\"small\" value={value} aria-label={ariaLabel}\n // quitar la selección no existe: un clic en la pestaña elegida no la apaga\n onChange={(event, nextValue) => nextValue !== null && onChange(nextValue)}>\n {options.map((option) => (\n <ToggleButton key={option.value} value={option.value} disabled={option.disabled}>{option.label}</ToggleButton>\n ))}\n </SegmentedControlRoot>\n </ScrollableRow>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAwBA,MAAM,kBAAkB;AACxB,MAAM,kCAAkC;AAEjC,MAAM,uBAAuB;AAAA,EAChC,KAAK;AAAA,EACL,GAAG;AAAA,EACH,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,cAAc;AAAA,EACd,UAAU;AAAA,EACV,MAAM;AAAA,EACN,2BAA2B;AAAA,IACvB,QAAQ;AAAA,IAAG,cAAc;AAAA,IAAS,GAAG;AAAA,IAAG,IAAI;AAAA,IAAM,IAAI;AAAA,IACtD,GAAG,WAAW,EAAE;AAAA,IAAG,YAAY;AAAA,IAAK,eAAe;AAAA,IAAQ,OAAO;AAAA,IAAkB,YAAY;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAKpG,wDAAwD,EAAC,QAAQ,GAAG,cAAc,SAAS,GAAG,EAAA;AAAA,EAC9F,oFAAoF;AAAA,IAChF,SAAS;AAAA,IAAgB,OAAO;AAAA,EAAA;AAExC;AAQA,SAAS,mBAAmB,OAAO;AAE/B,MAAI,MAAM,SAAS,SAAS,WAAW;AACnC,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,2BAA2B,EAAC,WAAW,gCAAA;AAAA,MACvC,oFAAoF;AAAA,QAChF,SAAS;AAAA,QAAoB,OAAO;AAAA,QAAgB,GAAG,MAAM,YAAY,QAAQ,EAAC,SAAS,6BAA4B;AAAA,MAAA;AAAA,IAC3H;AAAA,EAER;AACA,MAAI,CAAC,aAAa,KAAK,GAAG;AACtB,WAAO,CAAA;AAAA,EACX;AACA,SAAO;AAAA,IACH,QAAQ;AAAA;AAAA;AAAA,IAGR,2BAA2B,EAAC,WAAW,gCAAA;AAAA,IACvC,oFAAoF,EAAC,GAAG,YAAY,KAAK,GAAG,OAAO,eAAA;AAAA,EAAc;AAEzI;AAGA,MAAM,aAAa;AAGnB,MAAM,uBAAuB,OAAO,mBAAmB,EAAC,MAAM,YAAY,MAAM,QAAO,EAAE,CAAC,EAAC,MAAA,MACvF,MAAM,YAAY,CAAC,sBAAsB,mBAAmB,KAAK,CAAC,CAAC,CACtE;AAUD,SAAwB,iBAAiB,OAAO;AAE5C,QAAM,EAAC,SAAS,OAAO,UAAU,UAAA,IAAa,cAAc,EAAC,OAAO,MAAM,YAAW;AACrF;AAAA;AAAA;AAAA,IAGI,oBAAC,eAAA,EAAc,KAAK,GAAG,IAAI,iBAAiB,IAAI,EAAC,YAAY,UAAU,IAAI,CAAC,mBACxE,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAqB,WAAS;AAAA,QAAC,MAAK;AAAA,QAAQ;AAAA,QAAc,cAAY;AAAA,QAEpD,UAAU,CAAC,OAAO,cAAc,cAAc,QAAQ,SAAS,SAAS;AAAA,QACtF,kBAAQ,IAAI,CAAC,WACV,oBAAC,gBAAgC,OAAO,OAAO,OAAO,UAAU,OAAO,UAAW,UAAA,OAAO,MAAA,GAAtE,OAAO,KAAqE,CAClG;AAAA,MAAA;AAAA,IAAA,EACL,CACJ;AAAA;AAER;"}
|
|
@@ -4,15 +4,38 @@ import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
|
|
|
4
4
|
import Box from "@mui/material/Box";
|
|
5
5
|
import Chip from "@mui/material/Chip";
|
|
6
6
|
import Tooltip from "@mui/material/Tooltip";
|
|
7
|
+
import { styled, useThemeProps } from "@mui/material/styles";
|
|
7
8
|
import { lookup, labelOf, shortLabelOf } from "../../dictionaries/dictionaries.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
9
|
+
import { paletteOf } from "../../theme/components/paletteOf.js";
|
|
10
|
+
import { lightDarkStylesOf } from "../../theme/lightDarkStyles.js";
|
|
11
|
+
const THEME_NAME = "CalcitaStatusChip";
|
|
12
|
+
const PALETTE_TONES = ["primary", "secondary", "success", "warning", "error", "info"];
|
|
13
|
+
function paletteToneOf(theme, tone) {
|
|
14
|
+
return PALETTE_TONES.includes(tone) && paletteOf(theme)[tone] ? paletteOf(theme)[tone] : null;
|
|
15
|
+
}
|
|
16
|
+
const StatusChipRoot = styled("span", { name: THEME_NAME, slot: "Root" })(({ theme, ownerState }) => {
|
|
17
|
+
const color = paletteToneOf(theme, ownerState.tone);
|
|
18
|
+
return {
|
|
19
|
+
display: "inline-flex",
|
|
20
|
+
alignItems: "center",
|
|
21
|
+
gap: theme.spacing(0.75),
|
|
22
|
+
fontSize: 13,
|
|
23
|
+
fontWeight: 500,
|
|
24
|
+
whiteSpace: "nowrap",
|
|
25
|
+
...color ? lightDarkStylesOf(theme, { color: color.dark }, { color: color.light }) : { color: paletteOf(theme).text.secondary }
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
const StatusChipDot = styled("span", { name: THEME_NAME, slot: "Dot" })(({ theme, ownerState }) => ({
|
|
29
|
+
width: 6,
|
|
30
|
+
height: 6,
|
|
31
|
+
flexShrink: 0,
|
|
32
|
+
borderRadius: "50%",
|
|
33
|
+
backgroundColor: paletteToneOf(theme, ownerState.tone)?.main ?? paletteOf(theme).text.disabled
|
|
34
|
+
}));
|
|
35
|
+
const StatusChipLabel = styled("span", { name: THEME_NAME, slot: "Label" })({ minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" });
|
|
36
|
+
const StatusChipChip = styled(Chip, { name: THEME_NAME, slot: "Chip" })({});
|
|
37
|
+
function StatusChip(props) {
|
|
38
|
+
const { dictionary, value, variant = "dot", short = false, suffix, tooltip: customTooltip, sx } = useThemeProps({ props, name: THEME_NAME });
|
|
16
39
|
const { t } = useToolkitTranslation("dictionaries");
|
|
17
40
|
const dictionaryEntry = lookup(dictionary, value);
|
|
18
41
|
const fullLabel = labelOf(dictionaryEntry, t);
|
|
@@ -23,23 +46,10 @@ function StatusChip({ dictionary, value, variant = "dot", short = false, suffix,
|
|
|
23
46
|
/* @__PURE__ */ jsx(Box, { component: "span", sx: { fontWeight: 700 }, children: fullLabel }),
|
|
24
47
|
explanation && /* @__PURE__ */ jsx(Box, { component: "span", children: explanation })
|
|
25
48
|
] });
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
fontSize: 13,
|
|
31
|
-
fontWeight: 500,
|
|
32
|
-
whiteSpace: "nowrap",
|
|
33
|
-
color: dictionaryEntry.color === "default" ? "text.secondary" : `${dictionaryEntry.color}.dark`
|
|
34
|
-
}, children: [
|
|
35
|
-
/* @__PURE__ */ jsx(Box, { component: "span", "aria-hidden": "true", sx: {
|
|
36
|
-
width: 6,
|
|
37
|
-
height: 6,
|
|
38
|
-
flexShrink: 0,
|
|
39
|
-
borderRadius: "50%",
|
|
40
|
-
bgcolor: DOT_COLOR_BY_TONE[dictionaryEntry.color] ?? DOT_COLOR_BY_TONE.default
|
|
41
|
-
} }),
|
|
42
|
-
/* @__PURE__ */ jsx(Box, { component: "span", sx: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }, children: label })
|
|
49
|
+
const ownerState = { tone: dictionaryEntry.color, variant };
|
|
50
|
+
const content = variant === "chip" ? /* @__PURE__ */ jsx(StatusChipChip, { size: "small", label, color: dictionaryEntry.color, variant: "outlined", sx }) : /* @__PURE__ */ jsxs(StatusChipRoot, { ownerState, children: [
|
|
51
|
+
/* @__PURE__ */ jsx(StatusChipDot, { "aria-hidden": "true", ownerState }),
|
|
52
|
+
/* @__PURE__ */ jsx(StatusChipLabel, { ownerState, children: label })
|
|
43
53
|
] });
|
|
44
54
|
if (!tooltip) {
|
|
45
55
|
return content;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusChip.js","sources":["../../../src/ui/labels/StatusChip.jsx"],"sourcesContent":["// Estatus pintado desde un diccionario (dictionaries/dictionaries.js): etiqueta, tooltip y color salen del diccionario,\n// nunca del componente que lo usa. Un valor que no está en el diccionario se pinta con su código crudo.\n//\n// Dos formas:\n// - variant=\"dot\" (por defecto): punto de color + texto, sin fondo. Para la columna de estado de una tabla.\n// - variant=\"chip\": Chip de MUI con contorno, para formatos, hallazgos o encabezados.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Chip from '@mui/material/Chip';\nimport Tooltip from '@mui/material/Tooltip';\n\nimport {labelOf, lookup, shortLabelOf} from '@/dictionaries/dictionaries.js';\n\n// color del diccionario
|
|
1
|
+
{"version":3,"file":"StatusChip.js","sources":["../../../src/ui/labels/StatusChip.jsx"],"sourcesContent":["// Estatus pintado desde un diccionario (dictionaries/dictionaries.js): etiqueta, tooltip y color salen del diccionario,\n// nunca del componente que lo usa. Un valor que no está en el diccionario se pinta con su código crudo.\n//\n// Dos formas:\n// - variant=\"dot\" (por defecto): punto de color + texto, sin fondo. Para la columna de estado de una tabla.\n// - variant=\"chip\": Chip de MUI con contorno, para formatos, hallazgos o encabezados.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaStatusChip con defaultProps (p. ej.\n// {variant: 'chip'}) y styleOverrides de sus partes: root (el renglón del punto y el texto), dot, label y chip.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Chip from '@mui/material/Chip';\nimport Tooltip from '@mui/material/Tooltip';\nimport {styled, useThemeProps} from '@mui/material/styles';\n\nimport {labelOf, lookup, shortLabelOf} from '@/dictionaries/dictionaries.js';\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {lightDarkStylesOf} from '@/theme/lightDarkStyles.js';\n\n// el nombre de la pieza en el tema (theme.components.CalcitaStatusChip)\nconst THEME_NAME = 'CalcitaStatusChip';\n// los colores que trae la paleta; otro (o «default») se pinta neutro\nconst PALETTE_TONES = ['primary', 'secondary', 'success', 'warning', 'error', 'info'];\n\n/** El color del diccionario si la paleta lo tiene; si no, null (neutro). */\nfunction paletteToneOf(theme, tone) {\n return PALETTE_TONES.includes(tone) && paletteOf(theme)[tone] ? paletteOf(theme)[tone] : null;\n}\n\n// el punto y el texto. El texto va en el tono oscuro de su color en claro y en el claro en oscuro: con el oscuro también en\n// modo oscuro casi no se leía (el «Suspendido» rojo sobre la tabla oscura, lo vio el usuario). Con lightDarkStylesOf para\n// que el tema del proyecto (styleOverrides.root) le gane también en oscuro\nconst StatusChipRoot = styled('span', {name: THEME_NAME, slot: 'Root'})(({theme, ownerState}) => {\n const color = paletteToneOf(theme, ownerState.tone);\n return {\n display: 'inline-flex', alignItems: 'center', gap: theme.spacing(0.75),\n fontSize: 13, fontWeight: 500, whiteSpace: 'nowrap',\n ...(color\n ? lightDarkStylesOf(theme, {color: color.dark}, {color: color.light})\n : {color: paletteOf(theme).text.secondary}),\n };\n});\n\n// OJO: flexShrink 0; en una celda angosta el punto se encogía a 0 px y el estado perdía su color\nconst StatusChipDot = styled('span', {name: THEME_NAME, slot: 'Dot'})(({theme, ownerState}) => ({\n width: 6, height: 6, flexShrink: 0, borderRadius: '50%',\n backgroundColor: paletteToneOf(theme, ownerState.tone)?.main ?? paletteOf(theme).text.disabled,\n}));\n\n// el texto en su propio span: un nodo de texto suelto dentro de un flex se corta sin «…»\nconst StatusChipLabel = styled('span', {name: THEME_NAME, slot: 'Label'})({minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis'});\n\nconst StatusChipChip = styled(Chip, {name: THEME_NAME, slot: 'Chip'})({});\n\n/**\n * @param {object} props\n * @param {Record<string, object>} props.dictionary p. ej. BATCH_STATUS\n * @param {string|null|undefined} props.value código del API\n * @param {'dot'|'chip'} [props.variant]\n * @param {boolean} [props.short] pinta la palabra corta de la entrada (shortLabelKey), para una columna angosta; el\n * nombre completo queda en el tooltip, antes de su explicación\n * @param {string} [props.suffix] texto que va después de la etiqueta con \" · \" (p. ej. un conteo ya formateado)\n * @param {import('react').ReactNode} [props.tooltip] SUSTITUYE al tooltip del diccionario; úsalo cuando el renglón tiene algo más\n * que decir. OJO: no envuelvas este componente en otro Tooltip, porque el suyo sigue ahí y se ven los dos encimados\n * @param {object} [props.sx] ajustes de estilo del Chip (solo variant=\"chip\")\n */\nexport default function StatusChip(props) {\n // lo que el proyecto puso en su tema (components.CalcitaStatusChip.defaultProps) y encima lo que llega aquí\n const {dictionary, value, variant = 'dot', short = false, suffix, tooltip: customTooltip, sx} = useThemeProps({props, name: THEME_NAME});\n const {t} = useToolkitTranslation('dictionaries');\n const dictionaryEntry = lookup(dictionary, value);\n const fullLabel = labelOf(dictionaryEntry, t);\n const entryLabel = short ? shortLabelOf(dictionaryEntry, t) : fullLabel;\n const label = suffix ? `${entryLabel} · ${suffix}` : entryLabel;\n // el de quien lo usa gana: si pasa uno, el del diccionario no se pinta y no hay dos globos\n const explanation = customTooltip ?? t(dictionaryEntry.tooltipKey, {defaultValue: ''});\n // la palabra corta deja fuera parte del nombre: el tooltip lo dice completo y, debajo, su explicación\n const tooltip = entryLabel === fullLabel ? explanation : (\n <Box component=\"span\" sx={{display: 'flex', flexDirection: 'column', gap: 0.25}}>\n <Box component=\"span\" sx={{fontWeight: 700}}>{fullLabel}</Box>\n {explanation && <Box component=\"span\">{explanation}</Box>}\n </Box>\n );\n\n const ownerState = {tone: dictionaryEntry.color, variant};\n const content = variant === 'chip' ? (\n <StatusChipChip size=\"small\" label={label} color={dictionaryEntry.color} variant=\"outlined\" sx={sx} />\n ) : (\n <StatusChipRoot ownerState={ownerState}>\n <StatusChipDot aria-hidden=\"true\" ownerState={ownerState} />\n <StatusChipLabel ownerState={ownerState}>{label}</StatusChipLabel>\n </StatusChipRoot>\n );\n\n // guard clause: sin tooltip no envolvemos (un Tooltip vacío igual agrega listeners)\n if (!tooltip) {\n return content;\n }\n return <Tooltip title={tooltip}>{content}</Tooltip>;\n}\n"],"names":[],"mappings":";;;;;;;;;;AAsBA,MAAM,aAAa;AAEnB,MAAM,gBAAgB,CAAC,WAAW,aAAa,WAAW,WAAW,SAAS,MAAM;AAGpF,SAAS,cAAc,OAAO,MAAM;AAChC,SAAO,cAAc,SAAS,IAAI,KAAK,UAAU,KAAK,EAAE,IAAI,IAAI,UAAU,KAAK,EAAE,IAAI,IAAI;AAC7F;AAKA,MAAM,iBAAiB,OAAO,QAAQ,EAAC,MAAM,YAAY,MAAM,OAAA,CAAO,EAAE,CAAC,EAAC,OAAO,iBAAgB;AAC7F,QAAM,QAAQ,cAAc,OAAO,WAAW,IAAI;AAClD,SAAO;AAAA,IACH,SAAS;AAAA,IAAe,YAAY;AAAA,IAAU,KAAK,MAAM,QAAQ,IAAI;AAAA,IACrE,UAAU;AAAA,IAAI,YAAY;AAAA,IAAK,YAAY;AAAA,IAC3C,GAAI,QACE,kBAAkB,OAAO,EAAC,OAAO,MAAM,QAAO,EAAC,OAAO,MAAM,MAAA,CAAM,IAClE,EAAC,OAAO,UAAU,KAAK,EAAE,KAAK,UAAA;AAAA,EAAS;AAErD,CAAC;AAGD,MAAM,gBAAgB,OAAO,QAAQ,EAAC,MAAM,YAAY,MAAM,MAAA,CAAM,EAAE,CAAC,EAAC,OAAO,kBAAiB;AAAA,EAC5F,OAAO;AAAA,EAAG,QAAQ;AAAA,EAAG,YAAY;AAAA,EAAG,cAAc;AAAA,EAClD,iBAAiB,cAAc,OAAO,WAAW,IAAI,GAAG,QAAQ,UAAU,KAAK,EAAE,KAAK;AAC1F,EAAE;AAGF,MAAM,kBAAkB,OAAO,QAAQ,EAAC,MAAM,YAAY,MAAM,QAAA,CAAQ,EAAE,EAAC,UAAU,GAAG,UAAU,UAAU,cAAc,YAAW;AAErI,MAAM,iBAAiB,OAAO,MAAM,EAAC,MAAM,YAAY,MAAM,QAAO,EAAE,EAAE;AAcxE,SAAwB,WAAW,OAAO;AAEtC,QAAM,EAAC,YAAY,OAAO,UAAU,OAAO,QAAQ,OAAO,QAAQ,SAAS,eAAe,OAAM,cAAc,EAAC,OAAO,MAAM,YAAW;AACvI,QAAM,EAAC,EAAA,IAAK,sBAAsB,cAAc;AAChD,QAAM,kBAAkB,OAAO,YAAY,KAAK;AAChD,QAAM,YAAY,QAAQ,iBAAiB,CAAC;AAC5C,QAAM,aAAa,QAAQ,aAAa,iBAAiB,CAAC,IAAI;AAC9D,QAAM,QAAQ,SAAS,GAAG,UAAU,MAAM,MAAM,KAAK;AAErD,QAAM,cAAc,iBAAiB,EAAE,gBAAgB,YAAY,EAAC,cAAc,IAAG;AAErF,QAAM,UAAU,eAAe,YAAY,cACvC,qBAAC,OAAI,WAAU,QAAO,IAAI,EAAC,SAAS,QAAQ,eAAe,UAAU,KAAK,QACtE,UAAA;AAAA,IAAA,oBAAC,KAAA,EAAI,WAAU,QAAO,IAAI,EAAC,YAAY,IAAA,GAAO,UAAA,UAAA,CAAU;AAAA,IACvD,eAAe,oBAAC,KAAA,EAAI,WAAU,QAAQ,UAAA,YAAA,CAAY;AAAA,EAAA,GACvD;AAGJ,QAAM,aAAa,EAAC,MAAM,gBAAgB,OAAO,QAAA;AACjD,QAAM,UAAU,YAAY,SACxB,oBAAC,gBAAA,EAAe,MAAK,SAAQ,OAAc,OAAO,gBAAgB,OAAO,SAAQ,YAAW,IAAQ,IAEpG,qBAAC,kBAAe,YACZ,UAAA;AAAA,IAAA,oBAAC,eAAA,EAAc,eAAY,QAAO,WAAA,CAAwB;AAAA,IAC1D,oBAAC,iBAAA,EAAgB,YAAyB,UAAA,MAAA,CAAM;AAAA,EAAA,GACpD;AAIJ,MAAI,CAAC,SAAS;AACV,WAAO;AAAA,EACX;AACA,SAAO,oBAAC,SAAA,EAAQ,OAAO,SAAU,UAAA,SAAQ;AAC7C;"}
|
package/dist/ui/labels/Tag.js
CHANGED
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import Chip from "@mui/material/Chip";
|
|
4
4
|
import Tooltip from "@mui/material/Tooltip";
|
|
5
|
-
|
|
5
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
6
|
+
const THEME_NAME = "CalcitaTag";
|
|
7
|
+
const TagChip = piecePart(THEME_NAME, "Root", Chip);
|
|
8
|
+
function Tag(props) {
|
|
9
|
+
const { label, tooltip, color = "default", variant = "outlined", size = "small", onDelete, sx, ...chipProps } = usePieceProps(props, THEME_NAME);
|
|
6
10
|
const chip = /* @__PURE__ */ jsx(
|
|
7
|
-
|
|
11
|
+
TagChip,
|
|
8
12
|
{
|
|
9
13
|
label,
|
|
10
14
|
size,
|
|
11
15
|
variant,
|
|
12
16
|
color,
|
|
13
17
|
onDelete,
|
|
14
|
-
|
|
18
|
+
ownSx: { fontWeight: 500 },
|
|
19
|
+
sx,
|
|
15
20
|
...chipProps
|
|
16
21
|
}
|
|
17
22
|
);
|