@stackcraftmx/calcita 0.30.1 → 0.32.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 +17 -0
- package/dist/DataTable/AuditTrailDialog.js +4 -1
- package/dist/DataTable/AuditTrailDialog.js.map +1 -1
- package/dist/DataTable/AuditTrailTable.js +4 -1
- package/dist/DataTable/AuditTrailTable.js.map +1 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +3 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +3 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +4 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
- package/dist/DataTable/ColumnFilters/TextColumnFilter.js +3 -1
- package/dist/DataTable/ColumnFilters/TextColumnFilter.js.map +1 -1
- package/dist/DataTable/EmptyState.js +3 -1
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +3 -1
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +3 -1
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +4 -1
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/columns.js +3 -1
- package/dist/DataTable/columns.js.map +1 -1
- package/dist/access/Can.js +3 -1
- package/dist/access/Can.js.map +1 -1
- package/dist/access/PermissionMatrix.js +3 -1
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/access/RequireAccess.js +3 -1
- package/dist/access/RequireAccess.js.map +1 -1
- package/dist/account/AccountProvider.js +3 -1
- package/dist/account/AccountProvider.js.map +1 -1
- package/dist/account/AccountSecuritySection.js +3 -1
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/DemoTotpCodeButton.js +3 -1
- package/dist/account/DemoTotpCodeButton.js.map +1 -1
- package/dist/account/ForgotPasswordScreen.js +3 -1
- package/dist/account/ForgotPasswordScreen.js.map +1 -1
- package/dist/account/ProfileScreen.js +4 -1
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/ResetPasswordScreen.js +4 -1
- package/dist/account/ResetPasswordScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +3 -1
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/appShell/AccountMenu.js +3 -1
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +4 -1
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +4 -1
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +3 -1
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActions.js +3 -1
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarSearch.js +3 -1
- package/dist/appShell/BarSearch.js.map +1 -1
- package/dist/appShell/HeaderTools.js +4 -1
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MenuLinks.js +3 -1
- package/dist/appShell/MenuLinks.js.map +1 -1
- package/dist/appShell/MobileTabBar.js +3 -1
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +3 -1
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +4 -1
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +3 -1
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +3 -1
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TabBarEditor.js +3 -1
- package/dist/appShell/TabBarEditor.js.map +1 -1
- package/dist/appShell/TopMenu.js +3 -1
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +3 -1
- package/dist/appShell/Wordmark.js.map +1 -1
- package/dist/appShell/pageTitle.js +3 -1
- package/dist/appShell/pageTitle.js.map +1 -1
- package/dist/appShell/shellSizes.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +3 -1
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +4 -1
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +3 -1
- package/dist/calendar/EventDetails.js.map +1 -1
- package/dist/datePickers/DateInput.js +4 -1
- package/dist/datePickers/DateInput.js.map +1 -1
- package/dist/downloads/DownloadManager.js +3 -1
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/feedback/ConfirmDialog.js +4 -1
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +3 -1
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +3 -1
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +4 -1
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/formatting.js +8 -5
- package/dist/formatting.js.map +1 -1
- package/dist/forms/FormProblemAlert.js +3 -1
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/FormServerProblem.js +3 -1
- package/dist/forms/FormServerProblem.js.map +1 -1
- package/dist/forms/FormWizard.js +3 -1
- package/dist/forms/FormWizard.js.map +1 -1
- package/dist/forms/auth/ChangePasswordForm.js +3 -1
- package/dist/forms/auth/ChangePasswordForm.js.map +1 -1
- package/dist/forms/auth/ForgotPasswordForm.js +3 -1
- package/dist/forms/auth/ForgotPasswordForm.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +3 -1
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/security/SecondStepForm.js +3 -1
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +3 -1
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/hookForm/FormProblemAlert.js +3 -1
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/hookForm/FormWizard.js +3 -1
- package/dist/hookForm/FormWizard.js.map +1 -1
- package/dist/inbox/InboxLayout.js +4 -1
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +4 -1
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +3 -1
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/index.js +6 -2
- package/dist/inputs/CatalogInput.js +4 -1
- package/dist/inputs/CatalogInput.js.map +1 -1
- package/dist/inputs/ColorInput.js +4 -1
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FieldFrame.js +3 -1
- package/dist/inputs/FieldFrame.js.map +1 -1
- package/dist/inputs/FormSection.js +5 -2
- package/dist/inputs/FormSection.js.map +1 -1
- package/dist/inputs/NumberInput.js +4 -1
- package/dist/inputs/NumberInput.js.map +1 -1
- package/dist/inputs/SelectInput.js +4 -1
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +4 -1
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/kanban/KanbanBoard.js +4 -1
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/maps/MapView.js +4 -1
- package/dist/maps/MapView.js.map +1 -1
- package/dist/pwa/InstallAppSetting.js +3 -1
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +3 -1
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +3 -1
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/security/ActivityLog.js +3 -1
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +3 -1
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +3 -1
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/theme/createToolkitTheme.js +4 -1
- package/dist/theme/createToolkitTheme.js.map +1 -1
- package/dist/toolkit/ComponentTexts.js +3 -1
- package/dist/toolkit/ComponentTexts.js.map +1 -1
- package/dist/toolkit/ToolkitProvider.js +24 -3
- package/dist/toolkit/ToolkitProvider.js.map +1 -1
- package/dist/toolkit/toolkitContext.js.map +1 -1
- package/dist/toolkit/toolkitSettings.js +9 -0
- package/dist/toolkit/toolkitSettings.js.map +1 -1
- package/dist/toolkit/unknownProps.js +33 -0
- package/dist/toolkit/unknownProps.js.map +1 -0
- package/dist/types/DataTable/AuditTrailTable.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/TextColumnFilter.d.ts +1 -1
- package/dist/types/DataTable/columns.d.ts +3 -1
- package/dist/types/access/Can.d.ts +1 -1
- package/dist/types/access/RequireAccess.d.ts +1 -1
- package/dist/types/account/AccountProvider.d.ts +1 -1
- package/dist/types/account/ForgotPasswordScreen.d.ts +1 -1
- package/dist/types/account/ResetPasswordScreen.d.ts +1 -1
- package/dist/types/appShell/MenuLinks.d.ts +1 -1
- package/dist/types/appShell/pageTitle.d.ts +1 -1
- package/dist/types/appShell/shellSizes.d.ts +3 -1
- package/dist/types/datePickers/DateInput.d.ts +1 -1
- package/dist/types/downloads/DownloadManager.d.ts +1 -1
- package/dist/types/formatting.d.ts +2 -1
- package/dist/types/forms/FormProblemAlert.d.ts +1 -1
- package/dist/types/forms/FormServerProblem.d.ts +1 -1
- package/dist/types/forms/FormWizard.d.ts +1 -1
- package/dist/types/forms/auth/ChangePasswordForm.d.ts +1 -1
- package/dist/types/forms/auth/ForgotPasswordForm.d.ts +1 -1
- package/dist/types/hookForm/FormProblemAlert.d.ts +1 -1
- package/dist/types/hookForm/FormWizard.d.ts +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/inputs/CatalogInput.d.ts +1 -1
- package/dist/types/inputs/FieldFrame.d.ts +1 -1
- package/dist/types/inputs/NumberInput.d.ts +1 -1
- package/dist/types/pwa/InstallAppSetting.d.ts +1 -1
- package/dist/types/theme/createToolkitTheme.d.ts +1 -1
- package/dist/types/toolkit/ComponentTexts.d.ts +1 -1
- package/dist/types/toolkit/ToolkitProvider.d.ts +4 -1
- package/dist/types/toolkit/toolkitContext.d.ts +2 -1
- package/dist/types/toolkit/toolkitSettings.d.ts +13 -2
- package/dist/types/toolkit/unknownProps.d.ts +23 -0
- package/dist/types/ui/actions/CopyButton.d.ts +1 -1
- package/dist/types/ui/actions/SecretField.d.ts +1 -1
- package/dist/types/ui/labels/DictionaryText.d.ts +1 -1
- package/dist/types/ui/layout/FullPageLoader.d.ts +1 -1
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +9 -8
- package/dist/types/ui/metrics/chartParts.d.ts +3 -3
- package/dist/types/ui/metrics/heatmapScale.d.ts +23 -0
- package/dist/types/ui/navigation/DocumentTitle.d.ts +1 -1
- package/dist/ui/actions/ActionsMenu.js +3 -1
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/CopyButton.js +3 -1
- package/dist/ui/actions/CopyButton.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +4 -1
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SecretField.js +3 -1
- package/dist/ui/actions/SecretField.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +3 -1
- package/dist/ui/actions/SelectionActions.js.map +1 -1
- package/dist/ui/filters/DateRangeFilter.js +3 -1
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/PeriodRangeSelector.js +3 -1
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +3 -1
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/filters/SegmentedControl.js +3 -1
- package/dist/ui/filters/SegmentedControl.js.map +1 -1
- package/dist/ui/labels/DictionaryText.js +3 -1
- package/dist/ui/labels/DictionaryText.js.map +1 -1
- package/dist/ui/labels/StatusChip.js +3 -1
- package/dist/ui/labels/StatusChip.js.map +1 -1
- package/dist/ui/layout/CollapsibleSection.js +3 -1
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +3 -1
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/DefinitionList.js +3 -1
- package/dist/ui/layout/DefinitionList.js.map +1 -1
- package/dist/ui/layout/DetailHeader.js +3 -1
- package/dist/ui/layout/DetailHeader.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +4 -1
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +3 -1
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/PageHeader.js +3 -1
- package/dist/ui/layout/PageHeader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +3 -1
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/layout/ProfileHeader.js +3 -1
- package/dist/ui/layout/ProfileHeader.js.map +1 -1
- package/dist/ui/layout/SectionCard.js +3 -1
- package/dist/ui/layout/SectionCard.js.map +1 -1
- package/dist/ui/metrics/BarList.js +3 -1
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +13 -4
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +4 -1
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +4 -1
- package/dist/ui/metrics/LineChart.js.map +1 -1
- package/dist/ui/metrics/MetricCard.js +4 -1
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +3 -1
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +7 -3
- package/dist/ui/metrics/chartParts.js.map +1 -1
- package/dist/ui/metrics/heatmapScale.js +15 -1
- package/dist/ui/metrics/heatmapScale.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +3 -1
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
- package/dist/ui/navigation/DocumentTitle.js +3 -1
- package/dist/ui/navigation/DocumentTitle.js.map +1 -1
- package/dist/ui/navigation/FontSizeToggle.js +3 -1
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
- package/dist/ui/navigation/LanguageToggle.js +3 -1
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +3 -1
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +3 -1
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/FileDropzone.js +4 -1
- package/dist/upload/FileDropzone.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormSection.js","sources":["../../src/inputs/FormSection.jsx"],"sourcesContent":["// El formulario agrupado de Apple (el de Ajustes y el de la hoja «Nuevo evento» de Calendario): una tarjeta redonda con\n// renglones, el título de la sección arriba de la tarjeta y la explicación abajo. Dentro de una FormSection cada campo del\n// framework (TextInput, NumberInput, SelectInput, CatalogInput, ColorInput, DateInput y sus versiones de formik y de\n// react-hook-form) se pinta solo como renglón: la etiqueta a la izquierda y el campo a la derecha.\n//\n// <FormSection title=\"Datos del cliente\" footer=\"Le enviaremos la factura al correo.\">\n// <FormTextField name=\"name\" label=\"Nombre\" />\n// <FormTextField name=\"email\" label=\"Correo\" />\n// <FormRow label=\"Recibir avisos\"><Switch /></FormRow>\n// </FormSection>\n//\n// Las reglas, de la guía de Apple (HIG «Text fields», «Pickers», «Combo boxes» y sus imágenes):\n// - la etiqueta siempre a la vista, a la izquierda (la pista se va al escribir; la etiqueta no);\n// - el campo es un «pozo» parejo con esquinas de 12 px (fieldWellStylesOf): gris tenue en claro y más claro que la\n// tarjeta en oscuro, con la letra clara, como el campo de la imagen de Apple. Antes iba transparente y el relleno solo\n// salía con el cursor encima, en cuadro: no se entendía dónde escribir (lo reportaron);\n// - la fecha es el mismo campo que los demás, a todo lo ancho (en cápsula al final del renglón quedaba descuadrada), con\n// la letra del texto (no del color del tema: en oscuro el naranja ahí «se veía horrible», lo reportaron);\n// - al escribir, un aro tenue del color principal y la etiqueta del color del tema; con error, aro y etiqueta en rojo y\n// el mensaje debajo del campo;\n// - una línea fina entre renglones, que empieza donde empieza el texto.\n//\n// Los campos pueden ir directo o dentro de lo que el proyecto necesite (un Grid de dos columnas, un Stack, un componente\n// suyo): cada renglón dibuja su propia línea ARRIBA y la tarjeta sube su contenido un píxel y lo recorta, así la línea del\n// primero queda escondida. OJO: antes la línea se dibujaba entre dos hermanos seguidos y con un Grid en medio desaparecían\n// todas (lo señalaron en una revisión: obligaba a «la forma Calcita»).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFormSection (sus partes root, title, card y\n// footer) y components.CalcitaFormRow (root, label y control), con styleOverrides y defaultProps. Los colores de los campos,\n// más fácil con createToolkitTheme({fields}).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Box from '@mui/material/Box';\nimport Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\nimport {styled, useThemeProps} from '@mui/material/styles';\n\nimport {FormSectionContext} from '@/inputs/formLayoutContext.js';\nimport {PANEL_RADIUS, accentTextStylesOf, fieldVariablesOf, fieldWellFromVariablesOf} from '@/theme/glassSurface.js';\n\n/** La clase de cada renglón: la sección le dibuja su línea de arriba, esté donde esté adentro de la tarjeta. */\nexport const FORM_ROW_CLASS = 'calcita-form-row';\n\n// el renglón: alto mínimo para el dedo, su aire arriba y abajo, y lo ancho de la columna de las etiquetas\nconst ROW_MIN_HEIGHT_PIXELS = 52;\nconst ROW_PADDING_BLOCK_PIXELS = 8;\nconst LABEL_COLUMN = Object.freeze({share: '38%', maxPixels: 200});\n// lo que la etiqueta baja para quedar en la misma línea que el texto del campo (el campo mide 40 px y su letra 24 px)\nconst LABEL_TOP_PIXELS = 8;\n// el aro al escribir y con error (por ciento del color) y su grosor\nconst RING = Object.freeze({focusPercent: 45, errorPercent: 60, pixels: 2});\n\n/**\n * Los campos de MUI dentro de un renglón: el pozo de Apple (fieldWellStylesOf), sin el borde de MUI ni el vidrio del tema,\n * con su aro al escribir. Los selectores van con «html body &&&» para ganarle a los ajustes del tema dentro de una\n * tarjeta, que con el lente llevan una etiqueta y hasta cuatro clases (con menos, el relleno del tema salía al pasar el\n * cursor, en cuadro: los «hovers espantosos» que reportaron).\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction rowControlSxOf(theme) {\n const palette = (theme.vars ?? theme).palette;\n const strong = (selectors) => selectors.map((selector) => `html body &&& ${selector}`).join(', ');\n const roots = ['.MuiOutlinedInput-root', '.MuiFilledInput-root', '.MuiPickersOutlinedInput-root'];\n const ringOf = (color, percent) => ({outline: `${RING.pixels}px solid color-mix(in srgb, ${color} ${percent}%, transparent)`, outlineOffset: 0});\n return {\n // los tonos van en variables del renglón (fieldVariablesOf, abajo en FormRow): aquí abajo una regla de oscuro no aplica\n [strong(roots)]: {...fieldWellFromVariablesOf(theme), outline: 'none'},\n [strong(roots.map((root) => `${root}.Mui-focused`))]: ringOf(palette.primary.main, RING.focusPercent),\n [strong(roots.map((root) => `${root}.Mui-error`))]: ringOf(palette.error.main, RING.errorPercent),\n [strong(['.MuiOutlinedInput-notchedOutline', '.MuiPickersOutlinedInput-notchedOutline'])]: {display: 'none'},\n [strong(['.MuiFilledInput-root::before', '.MuiFilledInput-root::after'])]: {display: 'none'},\n // la fecha: el mismo campo que los demás, a todo lo ancho de su columna (en cápsula al final del renglón quedaba\n // descuadrada, con su ayuda a la izquierda debajo de nada; lo reportaron), con el ícono del calendario discreto\n [strong(['.MuiPickersOutlinedInput-root'])]: {color: palette.text.primary},\n [strong(['.MuiPickersOutlinedInput-root .MuiInputAdornment-root .MuiSvgIcon-root'])]: {fontSize: '1.125rem', color: palette.text.secondary},\n // la ayuda y el error, debajo del campo; el renglón no guarda lugar para una ayuda vacía\n '& .MuiFormHelperText-root': {marginInline: theme.spacing(0.5), marginTop: '4px', marginBottom: '2px'},\n '& .MuiFormHelperText-root:has(> .notranslate:only-child)': {display: 'none'},\n };\n}\n\n// los nombres de las piezas en el tema (theme.components.CalcitaFormSection y CalcitaFormRow)\nconst SECTION_THEME_NAME = 'CalcitaFormSection';\nconst ROW_THEME_NAME = 'CalcitaFormRow';\n\n// el renglón: la etiqueta a la izquierda y el campo a la derecha, con la línea de arriba y los tonos de los campos\nconst FormRowRoot = styled(Box, {name: ROW_THEME_NAME, slot: 'Root'})(({theme}) => {\n const palette = (theme.vars ?? theme).palette;\n return theme.unstable_sx({\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-start',\n gap: 2,\n minHeight: ROW_MIN_HEIGHT_PIXELS,\n px: 2,\n py: `${ROW_PADDING_BLOCK_PIXELS}px`,\n '&:focus-within > label': accentTextStylesOf(theme),\n '&:has(.Mui-error) > label': {color: palette.error.main},\n // los tonos de los campos (los del proyecto con createToolkitTheme fields, o los de Apple) para sus campos\n ...fieldVariablesOf(theme),\n ...rowControlSxOf(theme),\n });\n});\n\nconst FormRowLabel = styled(Typography, {name: ROW_THEME_NAME, slot: 'Label'})(({theme}) => theme.unstable_sx({\n flex: `0 0 ${LABEL_COLUMN.share}`, maxWidth: LABEL_COLUMN.maxPixels, pt: `${LABEL_TOP_PIXELS}px`, lineHeight: 1.5, color: 'text.primary',\n}));\n\nconst FormRowControl = styled(Box, {name: ROW_THEME_NAME, slot: 'Control'})({\n flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', minHeight: 40,\n});\n\nconst FormSectionRoot = styled(Box, {name: SECTION_THEME_NAME, slot: 'Root'})({});\n\nconst FormSectionTitle = styled(Typography, {name: SECTION_THEME_NAME, slot: 'Title'})(({theme}) => theme.unstable_sx({px: 2, mb: 0.75, fontWeight: 600}));\n\n// la tarjeta: la línea de arriba de cada renglón, desde donde empieza el texto (la del primero la recorta la tarjeta)\nconst FormSectionCard = styled(Paper, {name: SECTION_THEME_NAME, slot: 'Card'})(({theme}) => ({\n borderRadius: PANEL_RADIUS,\n overflow: 'hidden',\n [`& .${FORM_ROW_CLASS}::before`]: {\n content: '\"\"', position: 'absolute', top: 0, right: 0, left: theme.spacing(2), height: '1px',\n backgroundColor: (theme.vars ?? theme).palette.divider,\n },\n}));\n\nconst FormSectionFooter = styled(Typography, {name: SECTION_THEME_NAME, slot: 'Footer'})(({theme}) => theme.unstable_sx({px: 2, mt: 0.75}));\n\n/**\n * Un renglón del formulario agrupado: la etiqueta a la izquierda y lo que sea a la derecha (un campo, un interruptor, un\n * texto). Los campos del framework lo usan solos dentro de una FormSection; FormRow suelto es para lo demás.\n *\n * @param {object} props\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {string} [props.htmlFor] el id del campo que nombra la etiqueta\n * @param {string} [props.labelId] el id de la etiqueta (para un campo que la nombra con aria-labelledby)\n * @param {import('react').ReactNode} props.children\n */\nexport function FormRow(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFormRow.defaultProps) y encima lo que llega aquí\n const {label, htmlFor, labelId, children} = useThemeProps({props, name: ROW_THEME_NAME});\n return (\n <FormRowRoot className={FORM_ROW_CLASS}>\n <FormRowLabel component=\"label\" id={labelId} htmlFor={htmlFor} variant=\"body1\">\n {label}\n </FormRowLabel>\n <FormRowControl>\n {children}\n </FormRowControl>\n </FormRowRoot>\n );\n}\n\n/**\n * @param {object} props\n * @param {string} [props.title] ya traducido; va arriba de la tarjeta\n * @param {import('react').ReactNode} [props.footer] la explicación, abajo de la tarjeta\n * @param {import('react').ReactNode} props.children los campos, directo o dentro de un Grid, un Stack o un componente propio\n */\nexport default function FormSection(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFormSection.defaultProps) y encima lo que llega aquí\n const {title, footer, children} = useThemeProps({props, name: SECTION_THEME_NAME});\n const titleId = useId();\n return (\n <FormSectionContext value>\n <FormSectionRoot component=\"section\" aria-labelledby={title ? titleId : undefined}>\n {title && (\n <FormSectionTitle id={titleId} variant=\"subtitle2\" component=\"h2\" color=\"text.secondary\">\n {title}\n </FormSectionTitle>\n )}\n <FormSectionCard variant=\"outlined\">\n {/* un píxel arriba: la línea de los renglones de hasta arriba queda fuera de la tarjeta (overflow hidden) */}\n <Box sx={{mt: '-1px'}}>{children}</Box>\n </FormSectionCard>\n {footer && (\n <FormSectionFooter variant=\"caption\" component=\"p\" color=\"text.secondary\">{footer}</FormSectionFooter>\n )}\n </FormSectionRoot>\n </FormSectionContext>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FormSection.js","sources":["../../src/inputs/FormSection.jsx"],"sourcesContent":["// El formulario agrupado de Apple (el de Ajustes y el de la hoja «Nuevo evento» de Calendario): una tarjeta redonda con\n// renglones, el título de la sección arriba de la tarjeta y la explicación abajo. Dentro de una FormSection cada campo del\n// framework (TextInput, NumberInput, SelectInput, CatalogInput, ColorInput, DateInput y sus versiones de formik y de\n// react-hook-form) se pinta solo como renglón: la etiqueta a la izquierda y el campo a la derecha.\n//\n// <FormSection title=\"Datos del cliente\" footer=\"Le enviaremos la factura al correo.\">\n// <FormTextField name=\"name\" label=\"Nombre\" />\n// <FormTextField name=\"email\" label=\"Correo\" />\n// <FormRow label=\"Recibir avisos\"><Switch /></FormRow>\n// </FormSection>\n//\n// Las reglas, de la guía de Apple (HIG «Text fields», «Pickers», «Combo boxes» y sus imágenes):\n// - la etiqueta siempre a la vista, a la izquierda (la pista se va al escribir; la etiqueta no);\n// - el campo es un «pozo» parejo con esquinas de 12 px (fieldWellStylesOf): gris tenue en claro y más claro que la\n// tarjeta en oscuro, con la letra clara, como el campo de la imagen de Apple. Antes iba transparente y el relleno solo\n// salía con el cursor encima, en cuadro: no se entendía dónde escribir (lo reportaron);\n// - la fecha es el mismo campo que los demás, a todo lo ancho (en cápsula al final del renglón quedaba descuadrada), con\n// la letra del texto (no del color del tema: en oscuro el naranja ahí «se veía horrible», lo reportaron);\n// - al escribir, un aro tenue del color principal y la etiqueta del color del tema; con error, aro y etiqueta en rojo y\n// el mensaje debajo del campo;\n// - una línea fina entre renglones, que empieza donde empieza el texto.\n//\n// Los campos pueden ir directo o dentro de lo que el proyecto necesite (un Grid de dos columnas, un Stack, un componente\n// suyo): cada renglón dibuja su propia línea ARRIBA y la tarjeta sube su contenido un píxel y lo recorta, así la línea del\n// primero queda escondida. OJO: antes la línea se dibujaba entre dos hermanos seguidos y con un Grid en medio desaparecían\n// todas (lo señalaron en una revisión: obligaba a «la forma Calcita»).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFormSection (sus partes root, title, card y\n// footer) y components.CalcitaFormRow (root, label y control), con styleOverrides y defaultProps. Los colores de los campos,\n// más fácil con createToolkitTheme({fields}).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport Box from '@mui/material/Box';\nimport Paper from '@mui/material/Paper';\nimport Typography from '@mui/material/Typography';\nimport {styled, useThemeProps} from '@mui/material/styles';\n\nimport {FormSectionContext} from '@/inputs/formLayoutContext.js';\nimport {PANEL_RADIUS, accentTextStylesOf, fieldVariablesOf, fieldWellFromVariablesOf} from '@/theme/glassSurface.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n/** La clase de cada renglón: la sección le dibuja su línea de arriba, esté donde esté adentro de la tarjeta. */\nexport const FORM_ROW_CLASS = 'calcita-form-row';\n\n// el renglón: alto mínimo para el dedo, su aire arriba y abajo, y lo ancho de la columna de las etiquetas\nconst ROW_MIN_HEIGHT_PIXELS = 52;\nconst ROW_PADDING_BLOCK_PIXELS = 8;\nconst LABEL_COLUMN = Object.freeze({share: '38%', maxPixels: 200});\n// lo que la etiqueta baja para quedar en la misma línea que el texto del campo (el campo mide 40 px y su letra 24 px)\nconst LABEL_TOP_PIXELS = 8;\n// el aro al escribir y con error (por ciento del color) y su grosor\nconst RING = Object.freeze({focusPercent: 45, errorPercent: 60, pixels: 2});\n\n/**\n * Los campos de MUI dentro de un renglón: el pozo de Apple (fieldWellStylesOf), sin el borde de MUI ni el vidrio del tema,\n * con su aro al escribir. Los selectores van con «html body &&&» para ganarle a los ajustes del tema dentro de una\n * tarjeta, que con el lente llevan una etiqueta y hasta cuatro clases (con menos, el relleno del tema salía al pasar el\n * cursor, en cuadro: los «hovers espantosos» que reportaron).\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction rowControlSxOf(theme) {\n const palette = (theme.vars ?? theme).palette;\n const strong = (selectors) => selectors.map((selector) => `html body &&& ${selector}`).join(', ');\n const roots = ['.MuiOutlinedInput-root', '.MuiFilledInput-root', '.MuiPickersOutlinedInput-root'];\n const ringOf = (color, percent) => ({outline: `${RING.pixels}px solid color-mix(in srgb, ${color} ${percent}%, transparent)`, outlineOffset: 0});\n return {\n // los tonos van en variables del renglón (fieldVariablesOf, abajo en FormRow): aquí abajo una regla de oscuro no aplica\n [strong(roots)]: {...fieldWellFromVariablesOf(theme), outline: 'none'},\n [strong(roots.map((root) => `${root}.Mui-focused`))]: ringOf(palette.primary.main, RING.focusPercent),\n [strong(roots.map((root) => `${root}.Mui-error`))]: ringOf(palette.error.main, RING.errorPercent),\n [strong(['.MuiOutlinedInput-notchedOutline', '.MuiPickersOutlinedInput-notchedOutline'])]: {display: 'none'},\n [strong(['.MuiFilledInput-root::before', '.MuiFilledInput-root::after'])]: {display: 'none'},\n // la fecha: el mismo campo que los demás, a todo lo ancho de su columna (en cápsula al final del renglón quedaba\n // descuadrada, con su ayuda a la izquierda debajo de nada; lo reportaron), con el ícono del calendario discreto\n [strong(['.MuiPickersOutlinedInput-root'])]: {color: palette.text.primary},\n [strong(['.MuiPickersOutlinedInput-root .MuiInputAdornment-root .MuiSvgIcon-root'])]: {fontSize: '1.125rem', color: palette.text.secondary},\n // la ayuda y el error, debajo del campo; el renglón no guarda lugar para una ayuda vacía\n '& .MuiFormHelperText-root': {marginInline: theme.spacing(0.5), marginTop: '4px', marginBottom: '2px'},\n '& .MuiFormHelperText-root:has(> .notranslate:only-child)': {display: 'none'},\n };\n}\n\n// los nombres de las piezas en el tema (theme.components.CalcitaFormSection y CalcitaFormRow)\nconst SECTION_THEME_NAME = 'CalcitaFormSection';\nconst ROW_THEME_NAME = 'CalcitaFormRow';\n\n// el renglón: la etiqueta a la izquierda y el campo a la derecha, con la línea de arriba y los tonos de los campos\nconst FormRowRoot = styled(Box, {name: ROW_THEME_NAME, slot: 'Root'})(({theme}) => {\n const palette = (theme.vars ?? theme).palette;\n return theme.unstable_sx({\n position: 'relative',\n display: 'flex',\n alignItems: 'flex-start',\n gap: 2,\n minHeight: ROW_MIN_HEIGHT_PIXELS,\n px: 2,\n py: `${ROW_PADDING_BLOCK_PIXELS}px`,\n '&:focus-within > label': accentTextStylesOf(theme),\n '&:has(.Mui-error) > label': {color: palette.error.main},\n // los tonos de los campos (los del proyecto con createToolkitTheme fields, o los de Apple) para sus campos\n ...fieldVariablesOf(theme),\n ...rowControlSxOf(theme),\n });\n});\n\nconst FormRowLabel = styled(Typography, {name: ROW_THEME_NAME, slot: 'Label'})(({theme}) => theme.unstable_sx({\n flex: `0 0 ${LABEL_COLUMN.share}`, maxWidth: LABEL_COLUMN.maxPixels, pt: `${LABEL_TOP_PIXELS}px`, lineHeight: 1.5, color: 'text.primary',\n}));\n\nconst FormRowControl = styled(Box, {name: ROW_THEME_NAME, slot: 'Control'})({\n flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', minHeight: 40,\n});\n\nconst FormSectionRoot = styled(Box, {name: SECTION_THEME_NAME, slot: 'Root'})({});\n\nconst FormSectionTitle = styled(Typography, {name: SECTION_THEME_NAME, slot: 'Title'})(({theme}) => theme.unstable_sx({px: 2, mb: 0.75, fontWeight: 600}));\n\n// la tarjeta: la línea de arriba de cada renglón, desde donde empieza el texto (la del primero la recorta la tarjeta)\nconst FormSectionCard = styled(Paper, {name: SECTION_THEME_NAME, slot: 'Card'})(({theme}) => ({\n borderRadius: PANEL_RADIUS,\n overflow: 'hidden',\n [`& .${FORM_ROW_CLASS}::before`]: {\n content: '\"\"', position: 'absolute', top: 0, right: 0, left: theme.spacing(2), height: '1px',\n backgroundColor: (theme.vars ?? theme).palette.divider,\n },\n}));\n\nconst FormSectionFooter = styled(Typography, {name: SECTION_THEME_NAME, slot: 'Footer'})(({theme}) => theme.unstable_sx({px: 2, mt: 0.75}));\n\n/**\n * Un renglón del formulario agrupado: la etiqueta a la izquierda y lo que sea a la derecha (un campo, un interruptor, un\n * texto). Los campos del framework lo usan solos dentro de una FormSection; FormRow suelto es para lo demás.\n *\n * @param {object} props\n * @param {import('react').ReactNode} props.label ya traducida\n * @param {string} [props.htmlFor] el id del campo que nombra la etiqueta\n * @param {string} [props.labelId] el id de la etiqueta (para un campo que la nombra con aria-labelledby)\n * @param {import('react').ReactNode} props.children\n */\nexport function FormRow(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFormRow.defaultProps) y encima lo que llega aquí\n const {label, htmlFor, labelId, children, ...unknownProps} = useThemeProps({props, name: ROW_THEME_NAME});\n warnUnknownProps(ROW_THEME_NAME, unknownProps);\n return (\n <FormRowRoot className={FORM_ROW_CLASS}>\n <FormRowLabel component=\"label\" id={labelId} htmlFor={htmlFor} variant=\"body1\">\n {label}\n </FormRowLabel>\n <FormRowControl>\n {children}\n </FormRowControl>\n </FormRowRoot>\n );\n}\n\n/**\n * @param {object} props\n * @param {string} [props.title] ya traducido; va arriba de la tarjeta\n * @param {import('react').ReactNode} [props.footer] la explicación, abajo de la tarjeta\n * @param {import('react').ReactNode} props.children los campos, directo o dentro de un Grid, un Stack o un componente propio\n */\nexport default function FormSection(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFormSection.defaultProps) y encima lo que llega aquí\n const {title, footer, children, ...unknownProps} = useThemeProps({props, name: SECTION_THEME_NAME});\n warnUnknownProps(SECTION_THEME_NAME, unknownProps);\n const titleId = useId();\n return (\n <FormSectionContext value>\n <FormSectionRoot component=\"section\" aria-labelledby={title ? titleId : undefined}>\n {title && (\n <FormSectionTitle id={titleId} variant=\"subtitle2\" component=\"h2\" color=\"text.secondary\">\n {title}\n </FormSectionTitle>\n )}\n <FormSectionCard variant=\"outlined\">\n {/* un píxel arriba: la línea de los renglones de hasta arriba queda fuera de la tarjeta (overflow hidden) */}\n <Box sx={{mt: '-1px'}}>{children}</Box>\n </FormSectionCard>\n {footer && (\n <FormSectionFooter variant=\"caption\" component=\"p\" color=\"text.secondary\">{footer}</FormSectionFooter>\n )}\n </FormSectionRoot>\n </FormSectionContext>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AA2CO,MAAM,iBAAiB;AAG9B,MAAM,wBAAwB;AAC9B,MAAM,2BAA2B;AACjC,MAAM,eAAe,OAAO,OAAO,EAAC,OAAO,OAAO,WAAW,KAAI;AAEjE,MAAM,mBAAmB;AAEzB,MAAM,OAAO,OAAO,OAAO,EAAC,cAAc,IAAI,cAAc,IAAI,QAAQ,GAAE;AAW1E,SAAS,eAAe,OAAO;AAC3B,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,QAAM,SAAS,CAAC,cAAc,UAAU,IAAI,CAAC,aAAa,iBAAiB,QAAQ,EAAE,EAAE,KAAK,IAAI;AAChG,QAAM,QAAQ,CAAC,0BAA0B,wBAAwB,+BAA+B;AAChG,QAAM,SAAS,CAAC,OAAO,aAAa,EAAC,SAAS,GAAG,KAAK,MAAM,+BAA+B,KAAK,IAAI,OAAO,mBAAmB,eAAe;AAC7I,SAAO;AAAA;AAAA,IAEH,CAAC,OAAO,KAAK,CAAC,GAAG,EAAC,GAAG,yBAAyB,KAAK,GAAG,SAAS,OAAA;AAAA,IAC/D,CAAC,OAAO,MAAM,IAAI,CAAC,SAAS,GAAG,IAAI,cAAc,CAAC,CAAC,GAAG,OAAO,QAAQ,QAAQ,MAAM,KAAK,YAAY;AAAA,IACpG,CAAC,OAAO,MAAM,IAAI,CAAC,SAAS,GAAG,IAAI,YAAY,CAAC,CAAC,GAAG,OAAO,QAAQ,MAAM,MAAM,KAAK,YAAY;AAAA,IAChG,CAAC,OAAO,CAAC,oCAAoC,yCAAyC,CAAC,CAAC,GAAG,EAAC,SAAS,OAAA;AAAA,IACrG,CAAC,OAAO,CAAC,gCAAgC,6BAA6B,CAAC,CAAC,GAAG,EAAC,SAAS,OAAA;AAAA;AAAA;AAAA,IAGrF,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC,GAAG,EAAC,OAAO,QAAQ,KAAK,QAAA;AAAA,IAClE,CAAC,OAAO,CAAC,wEAAwE,CAAC,CAAC,GAAG,EAAC,UAAU,YAAY,OAAO,QAAQ,KAAK,UAAA;AAAA;AAAA,IAEjI,6BAA6B,EAAC,cAAc,MAAM,QAAQ,GAAG,GAAG,WAAW,OAAO,cAAc,MAAA;AAAA,IAChG,4DAA4D,EAAC,SAAS,OAAA;AAAA,EAAM;AAEpF;AAGA,MAAM,qBAAqB;AAC3B,MAAM,iBAAiB;AAGvB,MAAM,cAAc,OAAO,KAAK,EAAC,MAAM,gBAAgB,MAAM,OAAA,CAAO,EAAE,CAAC,EAAC,YAAW;AAC/E,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,SAAO,MAAM,YAAY;AAAA,IACrB,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,WAAW;AAAA,IACX,IAAI;AAAA,IACJ,IAAI,GAAG,wBAAwB;AAAA,IAC/B,0BAA0B,mBAAmB,KAAK;AAAA,IAClD,6BAA6B,EAAC,OAAO,QAAQ,MAAM,KAAA;AAAA;AAAA,IAEnD,GAAG,iBAAiB,KAAK;AAAA,IACzB,GAAG,eAAe,KAAK;AAAA,EAAA,CAC1B;AACL,CAAC;AAED,MAAM,eAAe,OAAO,YAAY,EAAC,MAAM,gBAAgB,MAAM,QAAA,CAAQ,EAAE,CAAC,EAAC,MAAA,MAAW,MAAM,YAAY;AAAA,EAC1G,MAAM,OAAO,aAAa,KAAK;AAAA,EAAI,UAAU,aAAa;AAAA,EAAW,IAAI,GAAG,gBAAgB;AAAA,EAAM,YAAY;AAAA,EAAK,OAAO;AAC9H,CAAC,CAAC;AAEF,MAAM,iBAAiB,OAAO,KAAK,EAAC,MAAM,gBAAgB,MAAM,UAAA,CAAU,EAAE;AAAA,EACxE,MAAM;AAAA,EAAG,UAAU;AAAA,EAAG,SAAS;AAAA,EAAQ,eAAe;AAAA,EAAU,gBAAgB;AAAA,EAAU,WAAW;AACzG,CAAC;AAED,MAAM,kBAAkB,OAAO,KAAK,EAAC,MAAM,oBAAoB,MAAM,QAAO,EAAE,EAAE;AAEhF,MAAM,mBAAmB,OAAO,YAAY,EAAC,MAAM,oBAAoB,MAAM,SAAQ,EAAE,CAAC,EAAC,YAAW,MAAM,YAAY,EAAC,IAAI,GAAG,IAAI,MAAM,YAAY,IAAA,CAAI,CAAC;AAGzJ,MAAM,kBAAkB,OAAO,OAAO,EAAC,MAAM,oBAAoB,MAAM,OAAA,CAAO,EAAE,CAAC,EAAC,aAAY;AAAA,EAC1F,cAAc;AAAA,EACd,UAAU;AAAA,EACV,CAAC,MAAM,cAAc,UAAU,GAAG;AAAA,IAC9B,SAAS;AAAA,IAAM,UAAU;AAAA,IAAY,KAAK;AAAA,IAAG,OAAO;AAAA,IAAG,MAAM,MAAM,QAAQ,CAAC;AAAA,IAAG,QAAQ;AAAA,IACvF,kBAAkB,MAAM,QAAQ,OAAO,QAAQ;AAAA,EAAA;AAEvD,EAAE;AAEF,MAAM,oBAAoB,OAAO,YAAY,EAAC,MAAM,oBAAoB,MAAM,SAAA,CAAS,EAAE,CAAC,EAAC,YAAW,MAAM,YAAY,EAAC,IAAI,GAAG,IAAI,KAAA,CAAK,CAAC;AAYnI,SAAS,QAAQ,OAAO;AAE3B,QAAM,EAAC,OAAO,SAAS,SAAS,UAAU,GAAG,aAAA,IAAgB,cAAc,EAAC,OAAO,MAAM,gBAAe;AACxG,mBAAiB,gBAAgB,YAAY;AAC7C,SACI,qBAAC,aAAA,EAAY,WAAW,gBACpB,UAAA;AAAA,IAAA,oBAAC,cAAA,EAAa,WAAU,SAAQ,IAAI,SAAS,SAAkB,SAAQ,SAClE,UAAA,MAAA,CACL;AAAA,IACA,oBAAC,kBACI,SAAA,CACL;AAAA,EAAA,GACJ;AAER;AAQA,SAAwB,YAAY,OAAO;AAEvC,QAAM,EAAC,OAAO,QAAQ,UAAU,GAAG,iBAAgB,cAAc,EAAC,OAAO,MAAM,mBAAA,CAAmB;AAClG,mBAAiB,oBAAoB,YAAY;AACjD,QAAM,UAAU,MAAA;AAChB,SACI,oBAAC,oBAAA,EAAmB,OAAK,MACrB,UAAA,qBAAC,iBAAA,EAAgB,WAAU,WAAU,mBAAiB,QAAQ,UAAU,QACnE,UAAA;AAAA,IAAA,SACG,oBAAC,kBAAA,EAAiB,IAAI,SAAS,SAAQ,aAAY,WAAU,MAAK,OAAM,kBACnE,UAAA,MAAA,CACL;AAAA,IAEJ,oBAAC,iBAAA,EAAgB,SAAQ,YAErB,UAAA,oBAAC,KAAA,EAAI,IAAI,EAAC,IAAI,OAAA,GAAU,SAAA,CAAS,EAAA,CACrC;AAAA,IACC,8BACI,mBAAA,EAAkB,SAAQ,WAAU,WAAU,KAAI,OAAM,kBAAkB,UAAA,OAAA,CAAO;AAAA,EAAA,EAAA,CAE1F,EAAA,CACJ;AAER;"}
|
|
@@ -7,6 +7,7 @@ import TextField from "@mui/material/TextField";
|
|
|
7
7
|
import { translateValidationMessage } from "../forms/validationMessages.js";
|
|
8
8
|
import FieldFrame from "./FieldFrame.js";
|
|
9
9
|
import { groupThousands, completeDecimals, cleanNumberText } from "./numberText.js";
|
|
10
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
10
11
|
const SEPARATORS_ROOM = 6;
|
|
11
12
|
function NumberInput({
|
|
12
13
|
name,
|
|
@@ -23,8 +24,10 @@ function NumberInput({
|
|
|
23
24
|
maxDigits = 17,
|
|
24
25
|
disabled = false,
|
|
25
26
|
inputRef,
|
|
26
|
-
texts
|
|
27
|
+
texts,
|
|
28
|
+
...unknownProps
|
|
27
29
|
}) {
|
|
30
|
+
warnUnknownProps("NumberInput", unknownProps);
|
|
28
31
|
const { t } = useToolkitTranslation("common", texts);
|
|
29
32
|
const generatedId = useId();
|
|
30
33
|
const inputId = `${name ?? "number"}-${generatedId}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.js","sources":["../../src/inputs/NumberInput.jsx"],"sourcesContent":["// Campo de NÚMERO suelto: se escribe y se lee con separador de miles («2,850,000.50») y al formulario (y al servidor)\n// llega el texto limpio («2850000.50»), que es lo que lee Money.fromString sin pasar por decimales.\n//\n// Por qué no type=\"number\": pinta flechitas que nadie usa, no agrupa miles, acepta «e» y en el teléfono deja escribir\n// cosas raras. Este usa el teclado numérico (inputMode decimal) y alinea a la derecha, como en una hoja de cálculo.\n//\n// Mientras se escribe se respeta lo tecleado (no completa decimales a medias); al salir se completan con ceros.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {cleanNumberText, completeDecimals, groupThousands} from '@/inputs/numberText.js';\n\n// lo que caben los separadores y el punto además de los dígitos\nconst SEPARATORS_ROOM = 6;\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|null|undefined} props.value el texto limpio ('2850000.50'); '' = vacío\n * @param {(value: string) => void} props.onChange recibe el texto limpio\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {number} [props.decimals] por omisión 2\n * @param {boolean} [props.allowNegative]\n * @param {import('react').ReactNode} [props.prefix] p. ej. «$»\n * @param {import('react').ReactNode} [props.suffix] p. ej. «MXN» o «%»\n * @param {number} [props.maxDigits] tope de dígitos (por omisión 17)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function NumberInput({\n name, label, value, onChange, onBlur, error, helperText = ' ', decimals = 2, allowNegative = false, prefix, suffix, maxDigits = 17,\n disabled = false, inputRef, texts,\n}) {\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'number'}-${generatedId}`;\n const [isTyping, setIsTyping] = useState(false);\n const errorText = translateValidationMessage(t, error);\n const valueText = value === null || value === undefined ? '' : String(value);\n // escribiendo: lo que hay; fuera del campo: con sus decimales completos\n const shownText = groupThousands(isTyping ? valueText : completeDecimals(valueText, decimals));\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n value={shownText}\n disabled={disabled}\n size=\"small\"\n fullWidth\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n onFocus={() => setIsTyping(true)}\n onChange={(event) => onChange(cleanNumberText(event.target.value, decimals, allowNegative))}\n onBlur={() => {\n setIsTyping(false);\n const completedText = completeDecimals(cleanNumberText(valueText, decimals, allowNegative), decimals);\n if (completedText !== valueText) {\n onChange(completedText);\n }\n onBlur?.();\n }}\n slotProps={{\n htmlInput: {inputMode: 'decimal', maxLength: maxDigits + SEPARATORS_ROOM, style: {textAlign: 'right'}},\n input: {\n startAdornment: prefix ? <InputAdornment position=\"start\">{prefix}</InputAdornment> : undefined,\n endAdornment: suffix ? <InputAdornment position=\"end\">{suffix}</InputAdornment> : undefined,\n },\n }}\n />\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NumberInput.js","sources":["../../src/inputs/NumberInput.jsx"],"sourcesContent":["// Campo de NÚMERO suelto: se escribe y se lee con separador de miles («2,850,000.50») y al formulario (y al servidor)\n// llega el texto limpio («2850000.50»), que es lo que lee Money.fromString sin pasar por decimales.\n//\n// Por qué no type=\"number\": pinta flechitas que nadie usa, no agrupa miles, acepta «e» y en el teléfono deja escribir\n// cosas raras. Este usa el teclado numérico (inputMode decimal) y alinea a la derecha, como en una hoja de cálculo.\n//\n// Mientras se escribe se respeta lo tecleado (no completa decimales a medias); al salir se completan con ceros.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport InputAdornment from '@mui/material/InputAdornment';\nimport TextField from '@mui/material/TextField';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {cleanNumberText, completeDecimals, groupThousands} from '@/inputs/numberText.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// lo que caben los separadores y el punto además de los dígitos\nconst SEPARATORS_ROOM = 6;\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|null|undefined} props.value el texto limpio ('2850000.50'); '' = vacío\n * @param {(value: string) => void} props.onChange recibe el texto limpio\n * @param {() => void} [props.onBlur]\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText]\n * @param {number} [props.decimals] por omisión 2\n * @param {boolean} [props.allowNegative]\n * @param {import('react').ReactNode} [props.prefix] p. ej. «$»\n * @param {import('react').ReactNode} [props.suffix] p. ej. «MXN» o «%»\n * @param {number} [props.maxDigits] tope de dígitos (por omisión 17)\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function NumberInput({\n name, label, value, onChange, onBlur, error, helperText = ' ', decimals = 2, allowNegative = false, prefix, suffix, maxDigits = 17,\n disabled = false, inputRef, texts,\n ...unknownProps\n}) {\n warnUnknownProps('NumberInput', unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'number'}-${generatedId}`;\n const [isTyping, setIsTyping] = useState(false);\n const errorText = translateValidationMessage(t, error);\n const valueText = value === null || value === undefined ? '' : String(value);\n // escribiendo: lo que hay; fuera del campo: con sus decimales completos\n const shownText = groupThousands(isTyping ? valueText : completeDecimals(valueText, decimals));\n\n return (\n <FieldFrame inputId={inputId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n value={shownText}\n disabled={disabled}\n size=\"small\"\n fullWidth\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n onFocus={() => setIsTyping(true)}\n onChange={(event) => onChange(cleanNumberText(event.target.value, decimals, allowNegative))}\n onBlur={() => {\n setIsTyping(false);\n const completedText = completeDecimals(cleanNumberText(valueText, decimals, allowNegative), decimals);\n if (completedText !== valueText) {\n onChange(completedText);\n }\n onBlur?.();\n }}\n slotProps={{\n htmlInput: {inputMode: 'decimal', maxLength: maxDigits + SEPARATORS_ROOM, style: {textAlign: 'right'}},\n input: {\n startAdornment: prefix ? <InputAdornment position=\"start\">{prefix}</InputAdornment> : undefined,\n endAdornment: suffix ? <InputAdornment position=\"end\">{suffix}</InputAdornment> : undefined,\n },\n }}\n />\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAoBA,MAAM,kBAAkB;AAoBxB,SAAwB,YAAY;AAAA,EAChC;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAO,aAAa;AAAA,EAAK,WAAW;AAAA,EAAG,gBAAgB;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ,YAAY;AAAA,EAChI,WAAW;AAAA,EAAO;AAAA,EAAU;AAAA,EAC5B,GAAG;AACP,GAAG;AACC,mBAAiB,eAAe,YAAY;AAC5C,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,QAAQ,IAAI,WAAW;AAClD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,YAAY,UAAU,QAAQ,UAAU,SAAY,KAAK,OAAO,KAAK;AAE3E,QAAM,YAAY,eAAe,WAAW,YAAY,iBAAiB,WAAW,QAAQ,CAAC;AAE7F,SACI,oBAAC,YAAA,EAAW,SAAkB,OAC1B,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA,MAAK;AAAA,MACL,WAAS;AAAA,MACT,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,aAAa;AAAA,MACzB,SAAS,MAAM,YAAY,IAAI;AAAA,MAC/B,UAAU,CAAC,UAAU,SAAS,gBAAgB,MAAM,OAAO,OAAO,UAAU,aAAa,CAAC;AAAA,MAC1F,QAAQ,MAAM;AACV,oBAAY,KAAK;AACjB,cAAM,gBAAgB,iBAAiB,gBAAgB,WAAW,UAAU,aAAa,GAAG,QAAQ;AACpG,YAAI,kBAAkB,WAAW;AAC7B,mBAAS,aAAa;AAAA,QAC1B;AACA,iBAAA;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACP,WAAW,EAAC,WAAW,WAAW,WAAW,YAAY,iBAAiB,OAAO,EAAC,WAAW,UAAO;AAAA,QACpG,OAAO;AAAA,UACH,gBAAgB,SAAS,oBAAC,kBAAe,UAAS,SAAS,kBAAO,IAAoB;AAAA,UACtF,cAAc,SAAS,oBAAC,kBAAe,UAAS,OAAO,kBAAO,IAAoB;AAAA,QAAA;AAAA,MACtF;AAAA,IACJ;AAAA,EAAA,GAER;AAER;"}
|
|
@@ -9,6 +9,7 @@ import Typography from "@mui/material/Typography";
|
|
|
9
9
|
import { translateValidationMessage } from "../forms/validationMessages.js";
|
|
10
10
|
import FieldFrame from "./FieldFrame.js";
|
|
11
11
|
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
12
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
12
13
|
const THEME_NAME = "CalcitaSelectInput";
|
|
13
14
|
const SelectOption = piecePart(THEME_NAME, "Option", MenuItem);
|
|
14
15
|
const SelectOptionText = piecePart(THEME_NAME, "OptionText", Box);
|
|
@@ -26,8 +27,10 @@ function SelectInput(props) {
|
|
|
26
27
|
disabled = false,
|
|
27
28
|
inputRef,
|
|
28
29
|
multiple = false,
|
|
29
|
-
texts
|
|
30
|
+
texts,
|
|
31
|
+
...unknownProps
|
|
30
32
|
} = usePieceProps(props, THEME_NAME);
|
|
33
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
31
34
|
const { t } = useToolkitTranslation("common", texts);
|
|
32
35
|
const generatedId = useId();
|
|
33
36
|
const inputId = `${name ?? "select"}-${generatedId}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectInput.js","sources":["../../src/inputs/SelectInput.jsx"],"sourcesContent":["// Lista desplegable suelta, con la etiqueta arriba (mismo look que TextInput): la que pintan FormSelectField (formik) y la\n// de react-hook-form, y la que se usa sin formulario.\n//\n// Cada opción puede traer una explicación (description) que se lee en la lista para escoger; en la caja queda solo el\n// nombre de lo elegido. OJO: sin renderValue, MUI copiaba la opción completa a la caja y se leía «MenudeoCompra por pieza…».\n//\n// Con `multiple` se eligen varias: la lista se queda abierta, cada elegida lleva su palomita y en la caja quedan sus nombres\n// separados por coma, en el orden de las opciones. Al formulario llega la lista de valores (Yup.array().min(1) la valida).\n// Para catálogos largos, CatalogInput con `multiple` (busca y deja chips).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSelectInput con defaultProps y\n// styleOverrides de sus partes: option, optionText.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport MenuItem from '@mui/material/MenuItem';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaSelectInput)\nconst THEME_NAME = 'CalcitaSelectInput';\nconst SelectOption = piecePart(THEME_NAME, 'Option', MenuItem);\nconst SelectOptionText = piecePart(THEME_NAME, 'OptionText', Box);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|Array<string|number>|null|undefined} props.value '' = sin elegir; con multiple, la lista ([] = ninguna)\n * @param {(value: any) => void} props.onChange recibe el valor elegido (con multiple, la lista), no el evento\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string}>} props.options\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] texto de la opción vacía\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias opciones\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SelectInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSelectInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, options, error, helperText = ' ', placeholder, disabled = false, inputRef, multiple = false, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'select'}-${generatedId}`;\n // OJO: la lista desplegable de MUI es un div y no un <input>: el `for` de la etiqueta no la une. Se nombra con su etiqueta\n // por aria-labelledby (labelId); sin esto el lector de pantalla no decía «Ciudad», solo lo elegido\n const labelId = `${inputId}-label`;\n const errorText = translateValidationMessage(t, error);\n const placeholderText = (\n <Typography component=\"span\"\n // OJO: el placeholder de un select activo se lee (text.secondary); text.disabled solo con el campo inactivo\n color={disabled ? 'text.disabled' : 'text.secondary'}>{placeholder ?? t('form.selectPlaceholder')}</Typography>\n );\n const renderSelectedValue = (selectedValue) => {\n if (multiple) {\n const selectedLabels = options.filter((option) => selectedValue.includes(option.value)).map((option) => option.label);\n return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholderText;\n }\n return options.find((option) => option.value === selectedValue)?.label ?? placeholderText;\n };\n const currentValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? '');\n // OJO: con multiple, el autollenado del navegador puede mandar el valor como texto separado por comas (lo dice MUI)\n const handleChange = (event) => {\n const nextValue = event.target.value;\n onChange(multiple && typeof nextValue === 'string' ? nextValue.split(',') : nextValue);\n };\n\n return (\n <FieldFrame inputId={inputId} labelId={labelId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n select\n size=\"small\"\n fullWidth\n disabled={disabled}\n value={currentValue}\n onChange={handleChange}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n slotProps={{select: {multiple, displayEmpty: true, renderValue: renderSelectedValue, labelId}}}>\n {/* la opción vacía solo con una: con varias, se quitan picándolas otra vez */}\n {!multiple && <MenuItem value=\"\" disabled>{placeholderText}</MenuItem>}\n {options.map((option) => (\n // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del\n // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)\n <SelectOption key={option.value} value={option.value} ownSx={{whiteSpace: 'normal'}}>\n <SelectOptionText component=\"span\" ownSx={{display: 'flex', flexDirection: 'column', minWidth: 0, flexGrow: 1}}>\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n {option.description && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n )}\n </SelectOptionText>\n </SelectOption>\n ))}\n </TextField>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SelectInput.js","sources":["../../src/inputs/SelectInput.jsx"],"sourcesContent":["// Lista desplegable suelta, con la etiqueta arriba (mismo look que TextInput): la que pintan FormSelectField (formik) y la\n// de react-hook-form, y la que se usa sin formulario.\n//\n// Cada opción puede traer una explicación (description) que se lee en la lista para escoger; en la caja queda solo el\n// nombre de lo elegido. OJO: sin renderValue, MUI copiaba la opción completa a la caja y se leía «MenudeoCompra por pieza…».\n//\n// Con `multiple` se eligen varias: la lista se queda abierta, cada elegida lleva su palomita y en la caja quedan sus nombres\n// separados por coma, en el orden de las opciones. Al formulario llega la lista de valores (Yup.array().min(1) la valida).\n// Para catálogos largos, CatalogInput con `multiple` (busca y deja chips).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaSelectInput con defaultProps y\n// styleOverrides de sus partes: option, optionText.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useId} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport MenuItem from '@mui/material/MenuItem';\nimport TextField from '@mui/material/TextField';\nimport Typography from '@mui/material/Typography';\n\nimport {translateValidationMessage} from '@/forms/validationMessages.js';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaSelectInput)\nconst THEME_NAME = 'CalcitaSelectInput';\nconst SelectOption = piecePart(THEME_NAME, 'Option', MenuItem);\nconst SelectOptionText = piecePart(THEME_NAME, 'OptionText', Box);\n\n/**\n * @param {object} props\n * @param {string} [props.name]\n * @param {string} props.label ya traducido\n * @param {string|number|Array<string|number>|null|undefined} props.value '' = sin elegir; con multiple, la lista ([] = ninguna)\n * @param {(value: any) => void} props.onChange recibe el valor elegido (con multiple, la lista), no el evento\n * @param {() => void} [props.onBlur]\n * @param {Array<{value: string|number, label: string, description?: string}>} props.options\n * @param {string|{key: string, values?: object}} [props.error]\n * @param {string} [props.helperText] ayuda fija; el error la reemplaza\n * @param {string} [props.placeholder] texto de la opción vacía\n * @param {boolean} [props.disabled]\n * @param {import('react').Ref<HTMLInputElement>} [props.inputRef]\n * @param {boolean} [props.multiple] elegir varias opciones\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function SelectInput(props) {\n // lo que el proyecto puso en su tema (components.CalcitaSelectInput.defaultProps) y encima lo que llega aquí\n const {\n name, label, value, onChange, onBlur, options, error, helperText = ' ', placeholder, disabled = false, inputRef, multiple = false, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const generatedId = useId();\n const inputId = `${name ?? 'select'}-${generatedId}`;\n // OJO: la lista desplegable de MUI es un div y no un <input>: el `for` de la etiqueta no la une. Se nombra con su etiqueta\n // por aria-labelledby (labelId); sin esto el lector de pantalla no decía «Ciudad», solo lo elegido\n const labelId = `${inputId}-label`;\n const errorText = translateValidationMessage(t, error);\n const placeholderText = (\n <Typography component=\"span\"\n // OJO: el placeholder de un select activo se lee (text.secondary); text.disabled solo con el campo inactivo\n color={disabled ? 'text.disabled' : 'text.secondary'}>{placeholder ?? t('form.selectPlaceholder')}</Typography>\n );\n const renderSelectedValue = (selectedValue) => {\n if (multiple) {\n const selectedLabels = options.filter((option) => selectedValue.includes(option.value)).map((option) => option.label);\n return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholderText;\n }\n return options.find((option) => option.value === selectedValue)?.label ?? placeholderText;\n };\n const currentValue = multiple ? (Array.isArray(value) ? value : []) : (value ?? '');\n // OJO: con multiple, el autollenado del navegador puede mandar el valor como texto separado por comas (lo dice MUI)\n const handleChange = (event) => {\n const nextValue = event.target.value;\n onChange(multiple && typeof nextValue === 'string' ? nextValue.split(',') : nextValue);\n };\n\n return (\n <FieldFrame inputId={inputId} labelId={labelId} label={label}>\n <TextField\n name={name}\n id={inputId}\n inputRef={inputRef}\n select\n size=\"small\"\n fullWidth\n disabled={disabled}\n value={currentValue}\n onChange={handleChange}\n onBlur={onBlur}\n error={Boolean(errorText)}\n helperText={errorText ?? helperText}\n slotProps={{select: {multiple, displayEmpty: true, renderValue: renderSelectedValue, labelId}}}>\n {/* la opción vacía solo con una: con varias, se quitan picándolas otra vez */}\n {!multiple && <MenuItem value=\"\" disabled>{placeholderText}</MenuItem>}\n {options.map((option) => (\n // la opción en renglón y su nombre con su explicación en un bloque: con la opción en columna, la palomita del\n // elegido (el tema la pinta al final) se bajaba a otro renglón (lo cachó una captura de Tareas)\n <SelectOption key={option.value} value={option.value} ownSx={{whiteSpace: 'normal'}}>\n <SelectOptionText component=\"span\" ownSx={{display: 'flex', flexDirection: 'column', minWidth: 0, flexGrow: 1}}>\n <Typography component=\"span\" variant=\"body2\">{option.label}</Typography>\n {option.description && (\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\">{option.description}</Typography>\n )}\n </SelectOptionText>\n </SelectOption>\n ))}\n </TextField>\n </FieldFrame>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,eAAe,UAAU,YAAY,UAAU,QAAQ;AAC7D,MAAM,mBAAmB,UAAU,YAAY,cAAc,GAAG;AAkBhE,SAAwB,YAAY,OAAO;AAEvC,QAAM;AAAA,IACN;AAAA,IAAM;AAAA,IAAO;AAAA,IAAO;AAAA,IAAU;AAAA,IAAQ;AAAA,IAAS;AAAA,IAAO,aAAa;AAAA,IAAK;AAAA,IAAa,WAAW;AAAA,IAAO;AAAA,IAAU,WAAW;AAAA,IAAO;AAAA,IACnI,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AAC/B,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,cAAc,MAAA;AACpB,QAAM,UAAU,GAAG,QAAQ,QAAQ,IAAI,WAAW;AAGlD,QAAM,UAAU,GAAG,OAAO;AAC1B,QAAM,YAAY,2BAA2B,GAAG,KAAK;AACrD,QAAM,kBACF;AAAA,IAAC;AAAA,IAAA;AAAA,MAAW,WAAU;AAAA,MAEV,OAAO,WAAW,kBAAkB;AAAA,MAAmB,UAAA,eAAe,EAAE,wBAAwB;AAAA,IAAA;AAAA,EAAA;AAEhH,QAAM,sBAAsB,CAAC,kBAAkB;AAC3C,QAAI,UAAU;AACV,YAAM,iBAAiB,QAAQ,OAAO,CAAC,WAAW,cAAc,SAAS,OAAO,KAAK,CAAC,EAAE,IAAI,CAAC,WAAW,OAAO,KAAK;AACpH,aAAO,eAAe,SAAS,IAAI,eAAe,KAAK,IAAI,IAAI;AAAA,IACnE;AACA,WAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,aAAa,GAAG,SAAS;AAAA,EAC9E;AACA,QAAM,eAAe,WAAY,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAO,SAAS;AAEhF,QAAM,eAAe,CAAC,UAAU;AAC5B,UAAM,YAAY,MAAM,OAAO;AAC/B,aAAS,YAAY,OAAO,cAAc,WAAW,UAAU,MAAM,GAAG,IAAI,SAAS;AAAA,EACzF;AAEA,SACI,oBAAC,YAAA,EAAW,SAAkB,SAAkB,OAC5C,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA,IAAI;AAAA,MACJ;AAAA,MACA,QAAM;AAAA,MACN,MAAK;AAAA,MACL,WAAS;AAAA,MACT;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,MACV;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,aAAa;AAAA,MACzB,WAAW,EAAC,QAAQ,EAAC,UAAU,cAAc,MAAM,aAAa,qBAAqB,UAAO;AAAA,MAE3F,UAAA;AAAA,QAAA,CAAC,YAAY,oBAAC,UAAA,EAAS,OAAM,IAAG,UAAQ,MAAE,UAAA,iBAAgB;AAAA,QAC1D,QAAQ,IAAI,CAAC;AAAA;AAAA;AAAA,UAGV,oBAAC,cAAA,EAAgC,OAAO,OAAO,OAAO,OAAO,EAAC,YAAY,SAAA,GACtE,UAAA,qBAAC,kBAAA,EAAiB,WAAU,QAAO,OAAO,EAAC,SAAS,QAAQ,eAAe,UAAU,UAAU,GAAG,UAAU,EAAA,GACxG,UAAA;AAAA,YAAA,oBAAC,cAAW,WAAU,QAAO,SAAQ,SAAS,iBAAO,OAAM;AAAA,YAC1D,OAAO,eACJ,oBAAC,YAAA,EAAW,WAAU,QAAO,SAAQ,WAAU,OAAM,kBAAkB,UAAA,OAAO,YAAA,CAAY;AAAA,UAAA,GAElG,EAAA,GANe,OAAO,KAO1B;AAAA,SACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAET;AAER;"}
|
|
@@ -9,6 +9,7 @@ import StepLabel from "@mui/material/StepLabel";
|
|
|
9
9
|
import Stepper from "@mui/material/Stepper";
|
|
10
10
|
import Typography from "@mui/material/Typography";
|
|
11
11
|
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
12
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
12
13
|
const THEME_NAME = "CalcitaWizardLayout";
|
|
13
14
|
const WizardStepper = piecePart(THEME_NAME, "Stepper", Stepper);
|
|
14
15
|
const WizardPhoneSteps = piecePart(THEME_NAME, "PhoneSteps", Box);
|
|
@@ -24,8 +25,10 @@ function WizardLayout(props) {
|
|
|
24
25
|
onSkip,
|
|
25
26
|
submitButton,
|
|
26
27
|
problemAlert,
|
|
27
|
-
texts
|
|
28
|
+
texts,
|
|
29
|
+
...unknownProps
|
|
28
30
|
} = usePieceProps(props, THEME_NAME);
|
|
31
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
29
32
|
const { t } = useToolkitTranslation("common", texts);
|
|
30
33
|
const activeStep = steps[activeIndex];
|
|
31
34
|
const isLastStep = activeIndex === steps.length - 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WizardLayout.js","sources":["../../src/inputs/WizardLayout.jsx"],"sourcesContent":["// Lo que se ve de un formulario por pasos: los pasos arriba (en el teléfono, «Paso 2 de 4» y su nombre), los campos del paso\n// y los botones (Anterior · Omitir este paso · Siguiente o Enviar). No sabe de formik ni de react-hook-form: el FormWizard de\n// cada uno (/forms y /react-hook-form) pone la lógica (qué revisa «Siguiente», qué hace «Omitir») y le pasa su botón de\n// enviar y su aviso de errores. Suelto sirve para armar un asistente propio con otra librería.\n//\n// Los textos se cambian con `texts`, con la forma de `common` en los textos del paquete: {wizard: {next: 'Continuar'}}.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWizardLayout con defaultProps y\n// styleOverrides de sus partes: stepper, phoneSteps, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Step from '@mui/material/Step';\nimport StepLabel from '@mui/material/StepLabel';\nimport Stepper from '@mui/material/Stepper';\nimport Typography from '@mui/material/Typography';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaWizardLayout)\nconst THEME_NAME = 'CalcitaWizardLayout';\nconst WizardStepper = piecePart(THEME_NAME, 'Stepper', Stepper);\nconst WizardPhoneSteps = piecePart(THEME_NAME, 'PhoneSteps', Box);\nconst WizardActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {number} props.activeIndex el paso que se ve\n * @param {Set<string>} props.skippedKeys los pasos omitidos\n * @param {boolean} [props.isSubmitting] mientras se envía, «Anterior» se apaga\n * @param {() => void} props.onBack\n * @param {() => void} props.onNext\n * @param {() => void} props.onSkip\n * @param {import('react').ReactNode} props.submitButton el botón de enviar del último paso\n * @param {import('react').ReactNode} [props.problemAlert] el aviso de errores del servidor, arriba de los botones del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function WizardLayout(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWizardLayout.defaultProps) y encima lo que llega aquí\n const {\n steps, activeIndex, skippedKeys, isSubmitting = false, onBack, onNext, onSkip, submitButton, problemAlert, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const activeStep = steps[activeIndex];\n const isLastStep = activeIndex === steps.length - 1;\n\n return (\n <Stack spacing={3}>\n {/* en el teléfono los nombres de los pasos no caben: se dice en qué paso va */}\n <WizardStepper activeStep={activeIndex} alternativeLabel ownSx={{display: {xs: 'none', sm: 'flex'}}}>\n {steps.map((step) => {\n let caption;\n if (skippedKeys.has(step.key)) {\n caption = t('wizard.skipped');\n } else if (step.optional) {\n caption = t('wizard.optional');\n }\n return (\n <Step key={step.key} completed={skippedKeys.has(step.key) ? false : undefined}>\n <StepLabel optional={caption && <Typography variant=\"caption\" color=\"text.secondary\">{caption}</Typography>}>\n {step.label}\n </StepLabel>\n </Step>\n );\n })}\n </WizardStepper>\n <WizardPhoneSteps ownSx={{display: {xs: 'block', sm: 'none'}}}>\n <Typography variant=\"overline\" color=\"text.secondary\">{t('wizard.stepOf', {current: activeIndex + 1, total: steps.length})}</Typography>\n <Typography variant=\"h6\">{activeStep.label}</Typography>\n </WizardPhoneSteps>\n\n <Box>{activeStep.content}</Box>\n\n {isLastStep && problemAlert}\n\n <WizardActions direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5} ownSx={{justifyContent: 'space-between'}}>\n <Button variant=\"text\" color=\"inherit\" disabled={activeIndex === 0 || isSubmitting} onClick={onBack}>\n {t('wizard.back')}\n </Button>\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5}>\n {activeStep.optional && !isLastStep && (\n <Button variant=\"text\" onClick={onSkip}>{t('wizard.skip')}</Button>\n )}\n {isLastStep\n ? submitButton\n : <Button variant=\"contained\" size=\"large\" onClick={onNext}>{t('wizard.next')}</Button>}\n </Stack>\n </WizardActions>\n </Stack>\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WizardLayout.js","sources":["../../src/inputs/WizardLayout.jsx"],"sourcesContent":["// Lo que se ve de un formulario por pasos: los pasos arriba (en el teléfono, «Paso 2 de 4» y su nombre), los campos del paso\n// y los botones (Anterior · Omitir este paso · Siguiente o Enviar). No sabe de formik ni de react-hook-form: el FormWizard de\n// cada uno (/forms y /react-hook-form) pone la lógica (qué revisa «Siguiente», qué hace «Omitir») y le pasa su botón de\n// enviar y su aviso de errores. Suelto sirve para armar un asistente propio con otra librería.\n//\n// Los textos se cambian con `texts`, con la forma de `common` en los textos del paquete: {wizard: {next: 'Continuar'}}.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaWizardLayout con defaultProps y\n// styleOverrides de sus partes: stepper, phoneSteps, actions.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Stack from '@mui/material/Stack';\nimport Step from '@mui/material/Step';\nimport StepLabel from '@mui/material/StepLabel';\nimport Stepper from '@mui/material/Stepper';\nimport Typography from '@mui/material/Typography';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaWizardLayout)\nconst THEME_NAME = 'CalcitaWizardLayout';\nconst WizardStepper = piecePart(THEME_NAME, 'Stepper', Stepper);\nconst WizardPhoneSteps = piecePart(THEME_NAME, 'PhoneSteps', Box);\nconst WizardActions = piecePart(THEME_NAME, 'Actions', Stack);\n\n/**\n * @param {object} props\n * @param {Object[]} props.steps en orden\n * @param {string} props.steps[].key\n * @param {string} props.steps[].label ya traducido\n * @param {boolean} [props.steps[].optional] se puede omitir\n * @param {import('react').ReactNode} props.steps[].content los campos del paso\n * @param {number} props.activeIndex el paso que se ve\n * @param {Set<string>} props.skippedKeys los pasos omitidos\n * @param {boolean} [props.isSubmitting] mientras se envía, «Anterior» se apaga\n * @param {() => void} props.onBack\n * @param {() => void} props.onNext\n * @param {() => void} props.onSkip\n * @param {import('react').ReactNode} props.submitButton el botón de enviar del último paso\n * @param {import('react').ReactNode} [props.problemAlert] el aviso de errores del servidor, arriba de los botones del último paso\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {wizard: {next, back, skip, optional, skipped, stepOf}}\n */\nexport default function WizardLayout(props) {\n // lo que el proyecto puso en su tema (components.CalcitaWizardLayout.defaultProps) y encima lo que llega aquí\n const {\n steps, activeIndex, skippedKeys, isSubmitting = false, onBack, onNext, onSkip, submitButton, problemAlert, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const activeStep = steps[activeIndex];\n const isLastStep = activeIndex === steps.length - 1;\n\n return (\n <Stack spacing={3}>\n {/* en el teléfono los nombres de los pasos no caben: se dice en qué paso va */}\n <WizardStepper activeStep={activeIndex} alternativeLabel ownSx={{display: {xs: 'none', sm: 'flex'}}}>\n {steps.map((step) => {\n let caption;\n if (skippedKeys.has(step.key)) {\n caption = t('wizard.skipped');\n } else if (step.optional) {\n caption = t('wizard.optional');\n }\n return (\n <Step key={step.key} completed={skippedKeys.has(step.key) ? false : undefined}>\n <StepLabel optional={caption && <Typography variant=\"caption\" color=\"text.secondary\">{caption}</Typography>}>\n {step.label}\n </StepLabel>\n </Step>\n );\n })}\n </WizardStepper>\n <WizardPhoneSteps ownSx={{display: {xs: 'block', sm: 'none'}}}>\n <Typography variant=\"overline\" color=\"text.secondary\">{t('wizard.stepOf', {current: activeIndex + 1, total: steps.length})}</Typography>\n <Typography variant=\"h6\">{activeStep.label}</Typography>\n </WizardPhoneSteps>\n\n <Box>{activeStep.content}</Box>\n\n {isLastStep && problemAlert}\n\n <WizardActions direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5} ownSx={{justifyContent: 'space-between'}}>\n <Button variant=\"text\" color=\"inherit\" disabled={activeIndex === 0 || isSubmitting} onClick={onBack}>\n {t('wizard.back')}\n </Button>\n <Stack direction={{xs: 'column-reverse', sm: 'row'}} spacing={1.5}>\n {activeStep.optional && !isLastStep && (\n <Button variant=\"text\" onClick={onSkip}>{t('wizard.skip')}</Button>\n )}\n {isLastStep\n ? submitButton\n : <Button variant=\"contained\" size=\"large\" onClick={onNext}>{t('wizard.next')}</Button>}\n </Stack>\n </WizardActions>\n </Stack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAuBA,MAAM,aAAa;AACnB,MAAM,gBAAgB,UAAU,YAAY,WAAW,OAAO;AAC9D,MAAM,mBAAmB,UAAU,YAAY,cAAc,GAAG;AAChE,MAAM,gBAAgB,UAAU,YAAY,WAAW,KAAK;AAmB5D,SAAwB,aAAa,OAAO;AAExC,QAAM;AAAA,IACN;AAAA,IAAO;AAAA,IAAa;AAAA,IAAa,eAAe;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAc;AAAA,IAAc;AAAA,IAC3G,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AAC/B,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,aAAa,MAAM,WAAW;AACpC,QAAM,aAAa,gBAAgB,MAAM,SAAS;AAElD,SACI,qBAAC,OAAA,EAAM,SAAS,GAEZ,UAAA;AAAA,IAAA,oBAAC,iBAAc,YAAY,aAAa,kBAAgB,MAAC,OAAO,EAAC,SAAS,EAAC,IAAI,QAAQ,IAAI,OAAA,EAAM,GAC5F,UAAA,MAAM,IAAI,CAAC,SAAS;AACjB,UAAI;AACJ,UAAI,YAAY,IAAI,KAAK,GAAG,GAAG;AAC3B,kBAAU,EAAE,gBAAgB;AAAA,MAChC,WAAW,KAAK,UAAU;AACtB,kBAAU,EAAE,iBAAiB;AAAA,MACjC;AACA,aACI,oBAAC,MAAA,EAAoB,WAAW,YAAY,IAAI,KAAK,GAAG,IAAI,QAAQ,QAChE,UAAA,oBAAC,WAAA,EAAU,UAAU,WAAW,oBAAC,YAAA,EAAW,SAAQ,WAAU,OAAM,kBAAkB,UAAA,QAAA,CAAQ,GACzF,UAAA,KAAK,MAAA,CACV,EAAA,GAHO,KAAK,GAIhB;AAAA,IAER,CAAC,EAAA,CACL;AAAA,IACA,qBAAC,kBAAA,EAAiB,OAAO,EAAC,SAAS,EAAC,IAAI,SAAS,IAAI,OAAA,EAAM,GACvD,UAAA;AAAA,MAAA,oBAAC,YAAA,EAAW,SAAQ,YAAW,OAAM,kBAAkB,UAAA,EAAE,iBAAiB,EAAC,SAAS,cAAc,GAAG,OAAO,MAAM,OAAA,CAAO,GAAE;AAAA,MAC3H,oBAAC,YAAA,EAAW,SAAQ,MAAM,qBAAW,MAAA,CAAM;AAAA,IAAA,GAC/C;AAAA,IAEA,oBAAC,KAAA,EAAK,UAAA,WAAW,QAAA,CAAQ;AAAA,IAExB,cAAc;AAAA,IAEf,qBAAC,eAAA,EAAc,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KAAK,OAAO,EAAC,gBAAgB,mBAC/F,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAO,SAAQ,QAAO,OAAM,WAAU,UAAU,gBAAgB,KAAK,cAAc,SAAS,QACxF,UAAA,EAAE,aAAa,GACpB;AAAA,MACA,qBAAC,OAAA,EAAM,WAAW,EAAC,IAAI,kBAAkB,IAAI,MAAA,GAAQ,SAAS,KACzD,UAAA;AAAA,QAAA,WAAW,YAAY,CAAC,cACrB,oBAAC,QAAA,EAAO,SAAQ,QAAO,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,QAE7D,aACK,eACA,oBAAC,QAAA,EAAO,SAAQ,aAAY,MAAK,SAAQ,SAAS,QAAS,UAAA,EAAE,aAAa,EAAA,CAAE;AAAA,MAAA,EAAA,CACtF;AAAA,IAAA,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;"}
|
|
@@ -28,6 +28,7 @@ import { cardsOfColumn, isOverLimit } from "./kanbanMoves.js";
|
|
|
28
28
|
import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
|
|
29
29
|
import ActionsMenu from "../ui/actions/ActionsMenu.js";
|
|
30
30
|
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
31
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
31
32
|
import { tagOf } from "../ui/labels/tagOf.js";
|
|
32
33
|
const THEME_NAME = "CalcitaKanbanBoard";
|
|
33
34
|
const KanbanCompleteButton = piecePart(THEME_NAME, "CompleteButton", IconButton);
|
|
@@ -192,8 +193,10 @@ function KanbanBoard(props) {
|
|
|
192
193
|
onCardComplete,
|
|
193
194
|
onColumnCollapse,
|
|
194
195
|
ariaLabel,
|
|
195
|
-
texts
|
|
196
|
+
texts,
|
|
197
|
+
...unknownProps
|
|
196
198
|
} = usePieceProps(props, THEME_NAME);
|
|
199
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
197
200
|
const { t } = useToolkitTranslation("kanban", texts);
|
|
198
201
|
const [addingColumnId, setAddingColumnId] = useState(null);
|
|
199
202
|
const [collapsedIds, setCollapsedIds] = useState(() => new Set(columns.filter((column) => column.defaultCollapsed).map((column) => column.id)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KanbanBoard.js","sources":["../../src/kanban/KanbanBoard.jsx"],"sourcesContent":["// Un tablero kanban (@stackcraftmx/calcita/kanban): columnas con tarjetas que se arrastran de una a otra con el ratón, con el\n// dedo o con el teclado (espacio para levantar, flechas para mover, espacio para soltar). Con @hello-pangea/dnd por dentro.\n//\n// <KanbanBoard columns={[{id: 'todo', title: 'Por hacer'}, {id: 'doing', title: 'En proceso', limit: 3}]}\n// cards={cards} onMove={(move) => api.post('/tasks/move', move)} ariaLabel=\"Tablero de tareas\" />\n//\n// El tablero no guarda nada: avisa la movida (onMove) y quien lo usa actualiza sus tarjetas (moveCard de kanbanMoves.js lo\n// hace sin esperar al servidor). Una columna con tope (limit, el trabajo en curso) avisa en ámbar cuando lo pasa.\n//\n// Accesible: el lector de pantalla anuncia en el idioma de la app qué se levantó, a dónde va y dónde quedó.\n//\n// Lo de un producto, todo hacia afuera (el tablero no guarda nada ni llama a ningún servidor):\n// - la tarjeta por omisión pinta lo que traiga: etiquetas (texto o {label, color}), responsables con su monograma, la\n// fecha límite (roja si ya venció, ámbar si es hoy), el avance de su lista de pendientes, comentarios y adjuntos. Para\n// otra tarjeta: renderCard;\n// - «+» en cada columna para agregar escribiendo el título (onAddCard({columnId, title}));\n// - «⋯» en cada tarjeta y en cada columna con las acciones que dé la pantalla (cardActions, columnActions): editar,\n// asignarme, mover sin arrastrar, borrar…;\n// - terminar una tarea: con onCardComplete cada tarjeta lleva su círculo para marcarla (y desmarcarla); qué pasa al\n// terminarla (pasarla a «Listo», guardar la fecha) lo decide la pantalla. Una tarjeta con completed: true sale con su\n// palomita verde y su fecha límite ya no se pinta de rojo;\n// - columnas que se encogen (collapsible), como el congelador (icebox) de ideas para después: se quedan en una tira angosta\n// con su nombre y su cuenta, y se les puede soltar una tarjeta igual. defaultCollapsed la abre encogida y\n// onColumnCollapse avisa cada cambio (para guardarlo si la pantalla quiere);\n// - texts para cualquier texto (la forma de `kanban` en los textos del paquete).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaKanbanBoard con defaultProps y\n// styleOverrides de sus partes: completeButton, tags, titleRow, doneIcon, cardTitle, meta, due, dueIcon, dueText,\n// checklist, checklistIcon, comments, commentsIcon, attachments, attachmentsIcon, assignees, root, collapsedColumn,\n// collapsedTitle, hidden, column, columnHeader, columnTitle, quickAdd, dropZone, card, cardActions, cardContent, empty.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {DragDropContext, Draggable, Droppable} from '@hello-pangea/dnd';\nimport Avatar from '@mui/material/Avatar';\nimport AvatarGroup from '@mui/material/AvatarGroup';\nimport Box from '@mui/material/Box';\nimport Chip from '@mui/material/Chip';\nimport IconButton from '@mui/material/IconButton';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\nimport ChatBubbleOutlineRoundedIcon from '@mui/icons-material/ChatBubbleOutlineRounded';\nimport CheckBoxOutlinedIcon from '@mui/icons-material/CheckBoxOutlined';\nimport CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';\nimport EventOutlinedIcon from '@mui/icons-material/EventOutlined';\nimport KeyboardDoubleArrowLeftRoundedIcon from '@mui/icons-material/KeyboardDoubleArrowLeftRounded';\nimport KeyboardDoubleArrowRightRoundedIcon from '@mui/icons-material/KeyboardDoubleArrowRightRounded';\nimport RadioButtonUncheckedRoundedIcon from '@mui/icons-material/RadioButtonUncheckedRounded';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {DUE_STATUS, dueStatusOf, tagOf, todayIsoDateOf} from '@/kanban/kanbanCards.js';\nimport {cardsOfColumn, isOverLimit} from '@/kanban/kanbanMoves.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ActionsMenu from '@/ui/actions/ActionsMenu.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaKanbanBoard)\nconst THEME_NAME = 'CalcitaKanbanBoard';\nconst KanbanCompleteButton = piecePart(THEME_NAME, 'CompleteButton', IconButton);\nconst KanbanTags = piecePart(THEME_NAME, 'Tags', Stack);\nconst KanbanTitleRow = piecePart(THEME_NAME, 'TitleRow', Stack);\nconst KanbanDoneIcon = piecePart(THEME_NAME, 'DoneIcon', CheckCircleRoundedIcon);\nconst KanbanCardTitle = piecePart(THEME_NAME, 'CardTitle', Typography);\nconst KanbanMeta = piecePart(THEME_NAME, 'Meta', Stack);\nconst KanbanDue = piecePart(THEME_NAME, 'Due', Stack);\nconst KanbanDueIcon = piecePart(THEME_NAME, 'DueIcon', EventOutlinedIcon);\nconst KanbanDueText = piecePart(THEME_NAME, 'DueText', Typography);\nconst KanbanChecklist = piecePart(THEME_NAME, 'Checklist', Stack);\nconst KanbanChecklistIcon = piecePart(THEME_NAME, 'ChecklistIcon', CheckBoxOutlinedIcon);\nconst KanbanComments = piecePart(THEME_NAME, 'Comments', Stack);\nconst KanbanCommentsIcon = piecePart(THEME_NAME, 'CommentsIcon', ChatBubbleOutlineRoundedIcon);\nconst KanbanAttachments = piecePart(THEME_NAME, 'Attachments', Stack);\nconst KanbanAttachmentsIcon = piecePart(THEME_NAME, 'AttachmentsIcon', AttachFileRoundedIcon);\nconst KanbanAssignees = piecePart(THEME_NAME, 'Assignees', AvatarGroup);\nconst KanbanRoot = piecePart(THEME_NAME, 'Root', Box);\nconst KanbanCollapsedColumn = piecePart(THEME_NAME, 'CollapsedColumn', Paper);\nconst KanbanCollapsedTitle = piecePart(THEME_NAME, 'CollapsedTitle', Typography);\nconst KanbanHidden = piecePart(THEME_NAME, 'Hidden', Box);\nconst KanbanColumn = piecePart(THEME_NAME, 'Column', Paper);\nconst KanbanColumnHeader = piecePart(THEME_NAME, 'ColumnHeader', Stack);\nconst KanbanColumnTitle = piecePart(THEME_NAME, 'ColumnTitle', Typography);\nconst KanbanQuickAdd = piecePart(THEME_NAME, 'QuickAdd', Box);\nconst KanbanDropZone = piecePart(THEME_NAME, 'DropZone', Stack);\nconst KanbanCard = piecePart(THEME_NAME, 'Card', Paper);\nconst KanbanCardActions = piecePart(THEME_NAME, 'CardActions', Box);\nconst KanbanCardContent = piecePart(THEME_NAME, 'CardContent', Box);\nconst KanbanEmpty = piecePart(THEME_NAME, 'Empty', Typography);\n\n// las columnas se reparten el ancho, sin bajar de este mínimo; si no caben, el tablero desliza de lado.\n// OJO: con el teclado, la librería solo mueve una tarjeta a columnas que se ven COMPLETAS: con columnas fijas de 300 px la\n// cuarta quedaba cortada en un escritorio y no se podía soltar ahí con el teclado\nconst COLUMN_MIN_WIDTH_PIXELS = 220;\n// lo alto que mide una columna vacía: lo justo para soltar ahí una tarjeta\nconst EMPTY_COLUMN_MIN_HEIGHT = 96;\n// cuántas caras de responsables se ven antes del «+2»\nconst MAX_VISIBLE_ASSIGNEES = 3;\nconst ASSIGNEE_AVATAR_PIXELS = 24;\n// lo ancho de una columna encogida: su nombre de lado, su cuenta y su botón para abrirla\nconst COLLAPSED_COLUMN_WIDTH_PIXELS = 52;\n// el color del texto de la fecha según cómo va\nconst DUE_COLOR = Object.freeze({[DUE_STATUS.overdue]: 'error.main', [DUE_STATUS.today]: 'warning.dark', [DUE_STATUS.upcoming]: 'text.secondary'});\n\n/**\n * El círculo para terminar una tarea (o reabrirla, si ya está terminada). El clic no levanta la tarjeta ni la abre.\n *\n * @param {{card: any, t: Function, onComplete: (card: any, completed: boolean) => void}} props\n */\nfunction CompleteButton({card, t, onComplete}) {\n const label = card.completed ? t('reopen', {title: card.title}) : t('complete', {title: card.title});\n return (\n <Tooltip title={label}>\n <KanbanCompleteButton size=\"small\" aria-label={label} aria-pressed={Boolean(card.completed)} ownSx={{m: -0.5, color: card.completed ? 'success.main' : 'text.secondary'}}\n onClick={(event) => {\n event.stopPropagation();\n onComplete(card, !card.completed);\n }}>\n {card.completed ? <CheckCircleRoundedIcon fontSize=\"small\" /> : <RadioButtonUncheckedRoundedIcon fontSize=\"small\" />}\n </KanbanCompleteButton>\n </Tooltip>\n );\n}\n\n/**\n * Una tarjeta por omisión: lo que traiga, sin pedir nada (título, descripción, etiquetas, responsables, fecha, pendientes,\n * comentarios y adjuntos).\n *\n * @param {{card: any, t: Function, today: string, onComplete?: (card: any, completed: boolean) => void}} props\n */\nfunction DefaultCard({card, t, today, onComplete}) {\n const {formatDate} = useFormatters();\n // terminada, su fecha ya no apura: se pinta tenue, sin rojo ni ámbar\n const dueStatus = card.completed && card.dueDate ? DUE_STATUS.upcoming : dueStatusOf(card.dueDate, today);\n let dueText = '';\n if (card.completed && card.dueDate) {\n dueText = t('dueCompleted', {date: formatDate(card.dueDate)});\n } else if (dueStatus === DUE_STATUS.overdue) {\n dueText = t('dueOverdue', {date: formatDate(card.dueDate)});\n } else if (dueStatus === DUE_STATUS.today) {\n dueText = t('dueToday');\n } else if (dueStatus) {\n dueText = t('dueOn', {date: formatDate(card.dueDate)});\n }\n const hasFooter = dueStatus || card.checklist?.total > 0 || card.comments > 0 || card.attachments > 0 || card.assignees?.length > 0;\n\n return (\n <Stack spacing={0.75}>\n {card.tags?.length > 0 && (\n <KanbanTags direction=\"row\" spacing={0.5} useFlexGap ownSx={{flexWrap: 'wrap'}}>\n {card.tags.map((rawTag) => {\n const tag = tagOf(rawTag);\n return <Chip key={tag.label} label={tag.label} size=\"small\" color={tag.color} variant={tag.color === 'default' ? 'filled' : 'outlined'} />;\n })}\n </KanbanTags>\n )}\n <KanbanTitleRow direction=\"row\" spacing={1} ownSx={{alignItems: 'flex-start'}}>\n {onComplete && <CompleteButton card={card} t={t} onComplete={onComplete} />}\n {!onComplete && card.completed && (\n <KanbanDoneIcon fontSize=\"small\" ownSx={{color: 'success.main'}} titleAccess={t('completed')} />\n )}\n <KanbanCardTitle variant=\"subtitle2\" component=\"p\" ownSx={{color: card.completed ? 'text.secondary' : undefined}}>{card.title}</KanbanCardTitle>\n </KanbanTitleRow>\n {card.description && <Typography variant=\"body2\" color=\"text.secondary\">{card.description}</Typography>}\n {hasFooter && (\n <KanbanMeta direction=\"row\" spacing={1.25} useFlexGap ownSx={{alignItems: 'center', flexWrap: 'wrap', color: 'text.secondary'}}>\n {dueStatus && (\n <KanbanDue direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center', color: DUE_COLOR[dueStatus]}}>\n <KanbanDueIcon ownSx={{fontSize: 16}} />\n <KanbanDueText variant=\"caption\" ownSx={{fontWeight: dueStatus === DUE_STATUS.upcoming ? 400 : 600}}>{dueText}</KanbanDueText>\n </KanbanDue>\n )}\n {card.checklist?.total > 0 && (\n <KanbanChecklist direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center'}}\n aria-label={t('checklist', {done: card.checklist.done, total: card.checklist.total})}>\n <KanbanChecklistIcon ownSx={{fontSize: 16}} />\n <Typography variant=\"caption\" aria-hidden=\"true\">{card.checklist.done}/{card.checklist.total}</Typography>\n </KanbanChecklist>\n )}\n {card.comments > 0 && (\n <KanbanComments direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center'}} aria-label={t('comments', {count: card.comments})}>\n <KanbanCommentsIcon ownSx={{fontSize: 16}} />\n <Typography variant=\"caption\" aria-hidden=\"true\">{card.comments}</Typography>\n </KanbanComments>\n )}\n {card.attachments > 0 && (\n <KanbanAttachments direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center'}} aria-label={t('attachments', {count: card.attachments})}>\n <KanbanAttachmentsIcon ownSx={{fontSize: 16}} />\n <Typography variant=\"caption\" aria-hidden=\"true\">{card.attachments}</Typography>\n </KanbanAttachments>\n )}\n {card.assignees?.length > 0 && (\n <KanbanAssignees max={MAX_VISIBLE_ASSIGNEES} ownSx={{ml: 'auto', '& .MuiAvatar-root': {width: ASSIGNEE_AVATAR_PIXELS, height: ASSIGNEE_AVATAR_PIXELS, fontSize: 11}}}\n aria-label={t('assignees', {names: card.assignees.map((person) => person.name).join(', ')})}>\n {card.assignees.map((person) => (\n <Tooltip key={person.id} title={person.name}>\n <Avatar alt={person.name} src={person.avatarUrl}>{initialsOf(person.name)}</Avatar>\n </Tooltip>\n ))}\n </KanbanAssignees>\n )}\n </KanbanMeta>\n )}\n </Stack>\n );\n}\n\n/**\n * Agregar una tarjeta escribiendo su título: Enter la agrega (y deja el campo listo para otra), Escape cierra.\n *\n * @param {{columnTitle: string, onAdd: (title: string) => void, onClose: () => void, t: Function}} props\n */\nfunction QuickAdd({columnTitle, onAdd, onClose, t}) {\n const [title, setTitle] = useState('');\n return (\n <TextField size=\"small\" fullWidth autoFocus multiline maxRows={4} value={title} placeholder={t('addCardPlaceholder')}\n slotProps={{htmlInput: {'aria-label': t('addCard', {column: columnTitle})}}}\n onChange={(event) => setTitle(event.target.value)}\n onBlur={() => !title.trim() && onClose()}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n onClose();\n }\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n if (title.trim()) {\n onAdd(title.trim());\n setTitle('');\n }\n }\n }} />\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, title: string, limit?: number, count?: number, collapsible?: boolean, defaultCollapsed?: boolean}>} props.columns\n * en su orden. count: cuántas tarjetas tiene de verdad cuando la pantalla le pasa solo las filtradas (el tope se cuenta\n * contra todas). collapsible: lleva su botón para encogerse (el congelador); defaultCollapsed: empieza encogida\n * @param {Array<{id: string, columnId: string, title: string, completed?: boolean, description?: string, tags?: Array<string|{label: string, color?: string}>, assignees?: Array<{id: string, name: string, avatarUrl?: string}>, dueDate?: string, checklist?: {done: number, total: number}, comments?: number, attachments?: number} & Record<string, any>>} props.cards\n * el orden de cada columna es el orden en este arreglo; cada tarjeta puede traer sus campos propios (llegan a renderCard,\n * a cardActions y a onCardClick tal cual). dueDate: AAAA-MM-DD\n * @param {(move: {cardId: string, fromColumnId: string, toColumnId: string, toIndex: number}) => void} props.onMove\n * @param {(card: any) => import('react').ReactNode} [props.renderCard] lo de adentro de cada tarjeta (recibe la tarjeta de cards)\n * @param {(cardId: string) => void} [props.onCardClick]\n * @param {(newCard: {columnId: string, title: string}) => void} [props.onAddCard] con él cada columna lleva su «+» para agregar\n * escribiendo el título\n * @param {(card: any) => Array<{key: string, label: string, icon?: import('react').ReactNode, color?: string, disabled?: boolean, onClick: () => void}>} [props.cardActions]\n * las acciones del «⋯» de cada tarjeta (editar, asignarme, borrar…); sin ellas no hay «⋯»\n * @param {(column: any) => Array<{key: string, label: string, icon?: import('react').ReactNode, color?: string, disabled?: boolean, onClick: () => void}>} [props.columnActions]\n * las acciones del «⋯» de cada columna (renombrar, limpiar…)\n * @param {(card: any, completed: boolean) => void} [props.onCardComplete] con él cada tarjeta lleva su círculo para terminarla\n * (completed true) o reabrirla (false); la pantalla decide qué más pasa (moverla a «Listo», guardar la fecha)\n * @param {(columnId: string, collapsed: boolean) => void} [props.onColumnCollapse] cada que una columna se encoge o se abre\n * @param {string} props.ariaLabel ya traducido\n * @param {object} [props.texts] los textos que cambian, con la forma de `kanban` en los textos del paquete\n */\nexport default function KanbanBoard(props) {\n // lo que el proyecto puso en su tema (components.CalcitaKanbanBoard.defaultProps) y encima lo que llega aquí\n const {\n columns, cards, onMove, renderCard, onCardClick, onAddCard, cardActions, columnActions, onCardComplete, onColumnCollapse, ariaLabel, texts,\n} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('kanban', texts);\n // la columna donde se está escribiendo una tarjeta nueva\n const [addingColumnId, setAddingColumnId] = useState(null);\n // las columnas encogidas: empiezan como dice defaultCollapsed y luego las mueve la persona\n const [collapsedIds, setCollapsedIds] = useState(() => new Set(columns.filter((column) => column.defaultCollapsed).map((column) => column.id)));\n const toggleColumn = (columnId) => {\n const collapsed = !collapsedIds.has(columnId);\n setCollapsedIds((current) => {\n const next = new Set(current);\n if (collapsed) {\n next.add(columnId);\n } else {\n next.delete(columnId);\n }\n return next;\n });\n onColumnCollapse?.(columnId, collapsed);\n };\n const today = todayIsoDateOf();\n const columnTitleOf = (columnId) => columns.find((column) => column.id === columnId)?.title ?? columnId;\n const cardTitleOf = (cardId) => cards.find((card) => card.id === cardId)?.title ?? cardId;\n\n // lo que oye quien usa lector de pantalla (posiciones desde 1, como se cuentan)\n const announceStart = (start, provided) => provided.announce(t('lifted', {\n title: cardTitleOf(start.draggableId), column: columnTitleOf(start.source.droppableId), position: start.source.index + 1,\n }));\n const announceUpdate = (update, provided) => {\n if (update.destination) {\n provided.announce(t('moving', {column: columnTitleOf(update.destination.droppableId), position: update.destination.index + 1}));\n }\n };\n const finishMove = (result, provided) => {\n const {draggableId, source, destination} = result;\n // se soltó fuera de una columna o en el mismo lugar: nada que mover\n if (!destination || (destination.droppableId === source.droppableId && destination.index === source.index)) {\n provided.announce(t('cancelled', {title: cardTitleOf(draggableId)}));\n return;\n }\n provided.announce(t('dropped', {title: cardTitleOf(draggableId), column: columnTitleOf(destination.droppableId), position: destination.index + 1}));\n onMove({cardId: draggableId, fromColumnId: source.droppableId, toColumnId: destination.droppableId, toIndex: destination.index});\n };\n\n return (\n <DragDropContext onDragStart={announceStart} onDragUpdate={announceUpdate} onDragEnd={finishMove} dragHandleUsageInstructions={t('instructions')}>\n <KanbanRoot role=\"region\" aria-label={ariaLabel} ownSx={{\n display: 'flex', gap: 2, overflowX: 'auto', pb: 1, scrollSnapType: 'x proximity', alignItems: 'flex-start',\n }}>\n {columns.map((column) => {\n const columnCards = cardsOfColumn(cards, column.id);\n // con un filtro puesto la pantalla dice cuántas tiene de verdad: «1 / 3» mentía con la columna llena\n const total = Number.isInteger(column.count) ? column.count : columnCards.length;\n const isOver = isOverLimit(column, total);\n const count = Number.isInteger(column.limit) ? `${total} / ${column.limit}` : String(total);\n const isCollapsed = column.collapsible && collapsedIds.has(column.id);\n // encogida: una tira con su nombre de lado y su cuenta; se le puede soltar una tarjeta (queda arriba)\n if (isCollapsed) {\n return (\n <Droppable key={column.id} droppableId={column.id}>\n {(droppableProvided, droppableSnapshot) => (\n <KanbanCollapsedColumn ref={droppableProvided.innerRef} {...droppableProvided.droppableProps} variant=\"outlined\" component=\"section\"\n aria-label={column.title}\n ownSx={{\n flex: '0 0 auto', width: COLLAPSED_COLUMN_WIDTH_PIXELS, py: 1, borderRadius: 3, display: 'flex',\n flexDirection: 'column', alignItems: 'center', gap: 1, minHeight: EMPTY_COLUMN_MIN_HEIGHT * 2,\n bgcolor: droppableSnapshot.isDraggingOver ? 'action.hover' : undefined,\n }}>\n <Tooltip title={t('expandColumn', {column: column.title})} placement=\"right\">\n <IconButton size=\"small\" aria-label={t('expandColumn', {column: column.title})} aria-expanded=\"false\"\n onClick={() => toggleColumn(column.id)}>\n <KeyboardDoubleArrowRightRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Chip size=\"small\" label={count} color={isOver ? 'warning' : 'default'} />\n <KanbanCollapsedTitle variant=\"subtitle2\" component=\"h3\" ownSx={{writingMode: 'vertical-rl', whiteSpace: 'nowrap'}}>\n {column.title}\n </KanbanCollapsedTitle>\n {/* el lugar de la tarjeta que se arrastra; encogida no la enseña */}\n <KanbanHidden ownSx={{display: 'none'}}>{droppableProvided.placeholder}</KanbanHidden>\n </KanbanCollapsedColumn>\n )}\n </Droppable>\n );\n }\n return (\n <KanbanColumn key={column.id} variant=\"outlined\" component=\"section\" aria-label={column.title}\n ownSx={{flex: '1 1 0', minWidth: COLUMN_MIN_WIDTH_PIXELS, p: 1.5, scrollSnapAlign: 'start', borderRadius: 3}}>\n <KanbanColumnHeader direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center', mb: 1.5, px: 0.5}}>\n <KanbanColumnTitle variant=\"subtitle2\" component=\"h3\" ownSx={{flexGrow: 1, minWidth: 0}}>{column.title}</KanbanColumnTitle>\n <Chip size=\"small\" label={count} color={isOver ? 'warning' : 'default'}\n title={isOver ? t('overLimit', {limit: column.limit}) : undefined} />\n {onAddCard && (\n <Tooltip title={t('addCard', {column: column.title})}>\n <IconButton size=\"small\" aria-label={t('addCard', {column: column.title})} onClick={() => setAddingColumnId(column.id)}>\n <AddRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <ActionsMenu label={t('columnActions', {column: column.title})} actions={columnActions?.(column)} />\n {column.collapsible && (\n <Tooltip title={t('collapseColumn', {column: column.title})}>\n <IconButton size=\"small\" aria-label={t('collapseColumn', {column: column.title})} aria-expanded=\"true\"\n onClick={() => toggleColumn(column.id)}>\n <KeyboardDoubleArrowLeftRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </KanbanColumnHeader>\n {addingColumnId === column.id && (\n <KanbanQuickAdd ownSx={{mb: 1}}>\n <QuickAdd columnTitle={column.title} t={t} onClose={() => setAddingColumnId(null)}\n onAdd={(title) => onAddCard({columnId: column.id, title})} />\n </KanbanQuickAdd>\n )}\n <Droppable droppableId={column.id}>\n {(droppableProvided, droppableSnapshot) => (\n <KanbanDropZone ref={droppableProvided.innerRef} {...droppableProvided.droppableProps} spacing={1}\n ownSx={{\n minHeight: EMPTY_COLUMN_MIN_HEIGHT, borderRadius: 2, transition: 'background-color 160ms',\n bgcolor: droppableSnapshot.isDraggingOver ? 'action.hover' : 'transparent',\n }}>\n {columnCards.map((card, index) => (\n <Draggable key={card.id} draggableId={card.id} index={index}>\n {(draggableProvided, draggableSnapshot) => (\n <KanbanCard ref={draggableProvided.innerRef} {...draggableProvided.draggableProps} {...draggableProvided.dragHandleProps}\n elevation={draggableSnapshot.isDragging ? 8 : 1}\n onClick={onCardClick ? () => onCardClick(card.id) : undefined}\n ownSx={{p: 1.5, borderRadius: 2, cursor: 'grab', position: 'relative', '&:focus-visible': {outline: 2, outlineColor: 'primary.main'}}}>\n {/* el «⋯» arriba a la derecha; el contenido le deja su lugar */}\n {cardActions && (\n <KanbanCardActions ownSx={{position: 'absolute', top: 4, right: 4}}>\n <ActionsMenu label={t('cardActions', {title: card.title})} actions={cardActions(card)} />\n </KanbanCardActions>\n )}\n <KanbanCardContent ownSx={cardActions ? {pr: 3} : undefined}>\n {renderCard ? renderCard(card) : <DefaultCard card={card} t={t} today={today} onComplete={onCardComplete} />}\n </KanbanCardContent>\n </KanbanCard>\n )}\n </Draggable>\n ))}\n {droppableProvided.placeholder}\n {/* vacía de verdad, o con tarjetas que el filtro de la pantalla esconde (no se dice «sin tarjetas») */}\n {columnCards.length === 0 && !droppableSnapshot.isDraggingOver && addingColumnId !== column.id && (\n <KanbanEmpty variant=\"caption\" color=\"text.secondary\" ownSx={{textAlign: 'center', py: 3}}>\n {total > 0 ? t('emptyFiltered') : t('empty')}\n </KanbanEmpty>\n )}\n </KanbanDropZone>\n )}\n </Droppable>\n </KanbanColumn>\n );\n })}\n </KanbanRoot>\n </DragDropContext>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,MAAM,aAAa;AACnB,MAAM,uBAAuB,UAAU,YAAY,kBAAkB,UAAU;AAC/E,MAAM,aAAa,UAAU,YAAY,QAAQ,KAAK;AACtD,MAAM,iBAAiB,UAAU,YAAY,YAAY,KAAK;AAC9D,MAAM,iBAAiB,UAAU,YAAY,YAAY,sBAAsB;AAC/E,MAAM,kBAAkB,UAAU,YAAY,aAAa,UAAU;AACrE,MAAM,aAAa,UAAU,YAAY,QAAQ,KAAK;AACtD,MAAM,YAAY,UAAU,YAAY,OAAO,KAAK;AACpD,MAAM,gBAAgB,UAAU,YAAY,WAAW,iBAAiB;AACxE,MAAM,gBAAgB,UAAU,YAAY,WAAW,UAAU;AACjE,MAAM,kBAAkB,UAAU,YAAY,aAAa,KAAK;AAChE,MAAM,sBAAsB,UAAU,YAAY,iBAAiB,oBAAoB;AACvF,MAAM,iBAAiB,UAAU,YAAY,YAAY,KAAK;AAC9D,MAAM,qBAAqB,UAAU,YAAY,gBAAgB,4BAA4B;AAC7F,MAAM,oBAAoB,UAAU,YAAY,eAAe,KAAK;AACpE,MAAM,wBAAwB,UAAU,YAAY,mBAAmB,qBAAqB;AAC5F,MAAM,kBAAkB,UAAU,YAAY,aAAa,WAAW;AACtE,MAAM,aAAa,UAAU,YAAY,QAAQ,GAAG;AACpD,MAAM,wBAAwB,UAAU,YAAY,mBAAmB,KAAK;AAC5E,MAAM,uBAAuB,UAAU,YAAY,kBAAkB,UAAU;AAC/E,MAAM,eAAe,UAAU,YAAY,UAAU,GAAG;AACxD,MAAM,eAAe,UAAU,YAAY,UAAU,KAAK;AAC1D,MAAM,qBAAqB,UAAU,YAAY,gBAAgB,KAAK;AACtE,MAAM,oBAAoB,UAAU,YAAY,eAAe,UAAU;AACzE,MAAM,iBAAiB,UAAU,YAAY,YAAY,GAAG;AAC5D,MAAM,iBAAiB,UAAU,YAAY,YAAY,KAAK;AAC9D,MAAM,aAAa,UAAU,YAAY,QAAQ,KAAK;AACtD,MAAM,oBAAoB,UAAU,YAAY,eAAe,GAAG;AAClE,MAAM,oBAAoB,UAAU,YAAY,eAAe,GAAG;AAClE,MAAM,cAAc,UAAU,YAAY,SAAS,UAAU;AAK7D,MAAM,0BAA0B;AAEhC,MAAM,0BAA0B;AAEhC,MAAM,wBAAwB;AAC9B,MAAM,yBAAyB;AAE/B,MAAM,gCAAgC;AAEtC,MAAM,YAAY,OAAO,OAAO,EAAC,CAAC,WAAW,OAAO,GAAG,cAAc,CAAC,WAAW,KAAK,GAAG,gBAAgB,CAAC,WAAW,QAAQ,GAAG,kBAAiB;AAOjJ,SAAS,eAAe,EAAC,MAAM,GAAG,cAAa;AAC3C,QAAM,QAAQ,KAAK,YAAY,EAAE,UAAU,EAAC,OAAO,KAAK,MAAA,CAAM,IAAI,EAAE,YAAY,EAAC,OAAO,KAAK,OAAM;AACnG,SACI,oBAAC,SAAA,EAAQ,OAAO,OACZ,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MAAqB,MAAK;AAAA,MAAQ,cAAY;AAAA,MAAO,gBAAc,QAAQ,KAAK,SAAS;AAAA,MAAG,OAAO,EAAC,GAAG,MAAM,OAAO,KAAK,YAAY,iBAAiB,iBAAA;AAAA,MAC3I,SAAS,CAAC,UAAU;AAChB,cAAM,gBAAA;AACN,mBAAW,MAAM,CAAC,KAAK,SAAS;AAAA,MACpC;AAAA,MACP,UAAA,KAAK,YAAY,oBAAC,wBAAA,EAAuB,UAAS,SAAQ,IAAK,oBAAC,iCAAA,EAAgC,UAAS,QAAA,CAAQ;AAAA,IAAA;AAAA,EAAA,GAE1H;AAER;AAQA,SAAS,YAAY,EAAC,MAAM,GAAG,OAAO,cAAa;AAC/C,QAAM,EAAC,WAAA,IAAc,cAAA;AAErB,QAAM,YAAY,KAAK,aAAa,KAAK,UAAU,WAAW,WAAW,YAAY,KAAK,SAAS,KAAK;AACxG,MAAI,UAAU;AACd,MAAI,KAAK,aAAa,KAAK,SAAS;AAChC,cAAU,EAAE,gBAAgB,EAAC,MAAM,WAAW,KAAK,OAAO,GAAE;AAAA,EAChE,WAAW,cAAc,WAAW,SAAS;AACzC,cAAU,EAAE,cAAc,EAAC,MAAM,WAAW,KAAK,OAAO,GAAE;AAAA,EAC9D,WAAW,cAAc,WAAW,OAAO;AACvC,cAAU,EAAE,UAAU;AAAA,EAC1B,WAAW,WAAW;AAClB,cAAU,EAAE,SAAS,EAAC,MAAM,WAAW,KAAK,OAAO,GAAE;AAAA,EACzD;AACA,QAAM,YAAY,aAAa,KAAK,WAAW,QAAQ,KAAK,KAAK,WAAW,KAAK,KAAK,cAAc,KAAK,KAAK,WAAW,SAAS;AAElI,SACI,qBAAC,OAAA,EAAM,SAAS,MACX,UAAA;AAAA,IAAA,KAAK,MAAM,SAAS,KACjB,oBAAC,cAAW,WAAU,OAAM,SAAS,KAAK,YAAU,MAAC,OAAO,EAAC,UAAU,OAAA,GAClE,eAAK,KAAK,IAAI,CAAC,WAAW;AACvB,YAAM,MAAM,MAAM,MAAM;AACxB,iCAAQ,MAAA,EAAqB,OAAO,IAAI,OAAO,MAAK,SAAQ,OAAO,IAAI,OAAO,SAAS,IAAI,UAAU,YAAY,WAAW,WAAA,GAA1G,IAAI,KAAkH;AAAA,IAC5I,CAAC,EAAA,CACL;AAAA,IAEJ,qBAAC,gBAAA,EAAe,WAAU,OAAM,SAAS,GAAG,OAAO,EAAC,YAAY,aAAA,GAC3D,UAAA;AAAA,MAAA,cAAc,oBAAC,gBAAA,EAAe,MAAY,GAAM,YAAwB;AAAA,MACxE,CAAC,cAAc,KAAK,aACjB,oBAAC,kBAAe,UAAS,SAAQ,OAAO,EAAC,OAAO,eAAA,GAAiB,aAAa,EAAE,WAAW,GAAG;AAAA,MAElG,oBAAC,iBAAA,EAAgB,SAAQ,aAAY,WAAU,KAAI,OAAO,EAAC,OAAO,KAAK,YAAY,mBAAmB,OAAA,GAAa,eAAK,MAAA,CAAM;AAAA,IAAA,GAClI;AAAA,IACC,KAAK,eAAe,oBAAC,YAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAkB,UAAA,KAAK,YAAA,CAAY;AAAA,IACzF,aACG,qBAAC,YAAA,EAAW,WAAU,OAAM,SAAS,MAAM,YAAU,MAAC,OAAO,EAAC,YAAY,UAAU,UAAU,QAAQ,OAAO,oBACxG,UAAA;AAAA,MAAA,aACG,qBAAC,WAAA,EAAU,WAAU,OAAM,SAAS,KAAK,OAAO,EAAC,YAAY,UAAU,OAAO,UAAU,SAAS,KAC7F,UAAA;AAAA,QAAA,oBAAC,eAAA,EAAc,OAAO,EAAC,UAAU,MAAK;AAAA,QACtC,oBAAC,eAAA,EAAc,SAAQ,WAAU,OAAO,EAAC,YAAY,cAAc,WAAW,WAAW,MAAM,IAAA,GAAO,UAAA,QAAA,CAAQ;AAAA,MAAA,GAClH;AAAA,MAEH,KAAK,WAAW,QAAQ,KACrB;AAAA,QAAC;AAAA,QAAA;AAAA,UAAgB,WAAU;AAAA,UAAM,SAAS;AAAA,UAAK,OAAO,EAAC,YAAY,SAAA;AAAA,UAC5D,cAAY,EAAE,aAAa,EAAC,MAAM,KAAK,UAAU,MAAM,OAAO,KAAK,UAAU,MAAA,CAAM;AAAA,UACtF,UAAA;AAAA,YAAA,oBAAC,qBAAA,EAAoB,OAAO,EAAC,UAAU,MAAK;AAAA,YAC5C,qBAAC,YAAA,EAAW,SAAQ,WAAU,eAAY,QAAQ,UAAA;AAAA,cAAA,KAAK,UAAU;AAAA,cAAK;AAAA,cAAE,KAAK,UAAU;AAAA,YAAA,EAAA,CAAM;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,MAGpG,KAAK,WAAW,KACb,qBAAC,kBAAe,WAAU,OAAM,SAAS,KAAK,OAAO,EAAC,YAAY,SAAA,GAAW,cAAY,EAAE,YAAY,EAAC,OAAO,KAAK,UAAS,GACzH,UAAA;AAAA,QAAA,oBAAC,oBAAA,EAAmB,OAAO,EAAC,UAAU,MAAK;AAAA,4BAC1C,YAAA,EAAW,SAAQ,WAAU,eAAY,QAAQ,eAAK,SAAA,CAAS;AAAA,MAAA,GACpE;AAAA,MAEH,KAAK,cAAc,KAChB,qBAAC,qBAAkB,WAAU,OAAM,SAAS,KAAK,OAAO,EAAC,YAAY,SAAA,GAAW,cAAY,EAAE,eAAe,EAAC,OAAO,KAAK,aAAY,GAClI,UAAA;AAAA,QAAA,oBAAC,uBAAA,EAAsB,OAAO,EAAC,UAAU,MAAK;AAAA,4BAC7C,YAAA,EAAW,SAAQ,WAAU,eAAY,QAAQ,eAAK,YAAA,CAAY;AAAA,MAAA,GACvE;AAAA,MAEH,KAAK,WAAW,SAAS,KACtB;AAAA,QAAC;AAAA,QAAA;AAAA,UAAgB,KAAK;AAAA,UAAuB,OAAO,EAAC,IAAI,QAAQ,qBAAqB,EAAC,OAAO,wBAAwB,QAAQ,wBAAwB,UAAU,GAAA,EAAE;AAAA,UACrJ,cAAY,EAAE,aAAa,EAAC,OAAO,KAAK,UAAU,IAAI,CAAC,WAAW,OAAO,IAAI,EAAE,KAAK,IAAI,GAAE;AAAA,UAClG,UAAA,KAAK,UAAU,IAAI,CAAC,+BAChB,SAAA,EAAwB,OAAO,OAAO,MACnC,UAAA,oBAAC,QAAA,EAAO,KAAK,OAAO,MAAM,KAAK,OAAO,WAAY,UAAA,WAAW,OAAO,IAAI,EAAA,CAAE,EAAA,GADhE,OAAO,EAErB,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,IACL,EAAA,CAER;AAAA,EAAA,GAER;AAER;AAOA,SAAS,SAAS,EAAC,aAAa,OAAO,SAAS,KAAI;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MAAU,MAAK;AAAA,MAAQ,WAAS;AAAA,MAAC,WAAS;AAAA,MAAC,WAAS;AAAA,MAAC,SAAS;AAAA,MAAG,OAAO;AAAA,MAAO,aAAa,EAAE,oBAAoB;AAAA,MACxG,WAAW,EAAC,WAAW,EAAC,cAAc,EAAE,WAAW,EAAC,QAAQ,YAAA,CAAY,IAAC;AAAA,MACzE,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,QAAQ,MAAM,CAAC,MAAM,KAAA,KAAU,QAAA;AAAA,MAC/B,WAAW,CAAC,UAAU;AAClB,YAAI,MAAM,QAAQ,UAAU;AACxB,kBAAA;AAAA,QACJ;AACA,YAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,UAAU;AAC1C,gBAAM,eAAA;AACN,cAAI,MAAM,QAAQ;AACd,kBAAM,MAAM,MAAM;AAClB,qBAAS,EAAE;AAAA,UACf;AAAA,QACJ;AAAA,MACJ;AAAA,IAAA;AAAA,EAAA;AAEnB;AAyBA,SAAwB,YAAY,OAAO;AAEvC,QAAM;AAAA,IACN;AAAA,IAAS;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAY;AAAA,IAAa;AAAA,IAAW;AAAA,IAAa;AAAA,IAAe;AAAA,IAAgB;AAAA,IAAkB;AAAA,IAAW;AAAA,EAAA,IACrI,cAAc,OAAO,UAAU;AAC/B,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AAEjD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,IAAI;AAEzD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,MAAM,IAAI,IAAI,QAAQ,OAAO,CAAC,WAAW,OAAO,gBAAgB,EAAE,IAAI,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC;AAC9I,QAAM,eAAe,CAAC,aAAa;AAC/B,UAAM,YAAY,CAAC,aAAa,IAAI,QAAQ;AAC5C,oBAAgB,CAAC,YAAY;AACzB,YAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,UAAI,WAAW;AACX,aAAK,IAAI,QAAQ;AAAA,MACrB,OAAO;AACH,aAAK,OAAO,QAAQ;AAAA,MACxB;AACA,aAAO;AAAA,IACX,CAAC;AACD,uBAAmB,UAAU,SAAS;AAAA,EAC1C;AACA,QAAM,QAAQ,eAAA;AACd,QAAM,gBAAgB,CAAC,aAAa,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,QAAQ,GAAG,SAAS;AAC/F,QAAM,cAAc,CAAC,WAAW,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,MAAM,GAAG,SAAS;AAGnF,QAAM,gBAAgB,CAAC,OAAO,aAAa,SAAS,SAAS,EAAE,UAAU;AAAA,IACrE,OAAO,YAAY,MAAM,WAAW;AAAA,IAAG,QAAQ,cAAc,MAAM,OAAO,WAAW;AAAA,IAAG,UAAU,MAAM,OAAO,QAAQ;AAAA,EAAA,CAC1H,CAAC;AACF,QAAM,iBAAiB,CAAC,QAAQ,aAAa;AACzC,QAAI,OAAO,aAAa;AACpB,eAAS,SAAS,EAAE,UAAU,EAAC,QAAQ,cAAc,OAAO,YAAY,WAAW,GAAG,UAAU,OAAO,YAAY,QAAQ,EAAA,CAAE,CAAC;AAAA,IAClI;AAAA,EACJ;AACA,QAAM,aAAa,CAAC,QAAQ,aAAa;AACrC,UAAM,EAAC,aAAa,QAAQ,YAAA,IAAe;AAE3C,QAAI,CAAC,eAAgB,YAAY,gBAAgB,OAAO,eAAe,YAAY,UAAU,OAAO,OAAQ;AACxG,eAAS,SAAS,EAAE,aAAa,EAAC,OAAO,YAAY,WAAW,EAAA,CAAE,CAAC;AACnE;AAAA,IACJ;AACA,aAAS,SAAS,EAAE,WAAW,EAAC,OAAO,YAAY,WAAW,GAAG,QAAQ,cAAc,YAAY,WAAW,GAAG,UAAU,YAAY,QAAQ,EAAA,CAAE,CAAC;AAClJ,WAAO,EAAC,QAAQ,aAAa,cAAc,OAAO,aAAa,YAAY,YAAY,aAAa,SAAS,YAAY,MAAA,CAAM;AAAA,EACnI;AAEA,6BACK,iBAAA,EAAgB,aAAa,eAAe,cAAc,gBAAgB,WAAW,YAAY,6BAA6B,EAAE,cAAc,GAC3I,UAAA,oBAAC,YAAA,EAAW,MAAK,UAAS,cAAY,WAAW,OAAO;AAAA,IACpD,SAAS;AAAA,IAAQ,KAAK;AAAA,IAAG,WAAW;AAAA,IAAQ,IAAI;AAAA,IAAG,gBAAgB;AAAA,IAAe,YAAY;AAAA,EAAA,GAE7F,UAAA,QAAQ,IAAI,CAAC,WAAW;AACrB,UAAM,cAAc,cAAc,OAAO,OAAO,EAAE;AAElD,UAAM,QAAQ,OAAO,UAAU,OAAO,KAAK,IAAI,OAAO,QAAQ,YAAY;AAC1E,UAAM,SAAS,YAAY,QAAQ,KAAK;AACxC,UAAM,QAAQ,OAAO,UAAU,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,KAAK,KAAK,OAAO,KAAK;AAC1F,UAAM,cAAc,OAAO,eAAe,aAAa,IAAI,OAAO,EAAE;AAEpE,QAAI,aAAa;AACb,iCACK,WAAA,EAA0B,aAAa,OAAO,IAC1C,UAAA,CAAC,mBAAmB,sBACjB;AAAA,QAAC;AAAA,QAAA;AAAA,UAAsB,KAAK,kBAAkB;AAAA,UAAW,GAAG,kBAAkB;AAAA,UAAgB,SAAQ;AAAA,UAAW,WAAU;AAAA,UACpH,cAAY,OAAO;AAAA,UACnB,OAAO;AAAA,YACH,MAAM;AAAA,YAAY,OAAO;AAAA,YAA+B,IAAI;AAAA,YAAG,cAAc;AAAA,YAAG,SAAS;AAAA,YACzF,eAAe;AAAA,YAAU,YAAY;AAAA,YAAU,KAAK;AAAA,YAAG,WAAW,0BAA0B;AAAA,YAC5F,SAAS,kBAAkB,iBAAiB,iBAAiB;AAAA,UAAA;AAAA,UAEpE,UAAA;AAAA,YAAA,oBAAC,SAAA,EAAQ,OAAO,EAAE,gBAAgB,EAAC,QAAQ,OAAO,MAAA,CAAM,GAAG,WAAU,SACjE,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBAAW,MAAK;AAAA,gBAAQ,cAAY,EAAE,gBAAgB,EAAC,QAAQ,OAAO,OAAM;AAAA,gBAAG,iBAAc;AAAA,gBAClF,SAAS,MAAM,aAAa,OAAO,EAAE;AAAA,gBAC7C,UAAA,oBAAC,qCAAA,EAAoC,UAAS,QAAA,CAAQ;AAAA,cAAA;AAAA,YAAA,GAE9D;AAAA,YACA,oBAAC,QAAK,MAAK,SAAQ,OAAO,OAAO,OAAO,SAAS,YAAY,UAAA,CAAW;AAAA,YACxE,oBAAC,sBAAA,EAAqB,SAAQ,aAAY,WAAU,MAAK,OAAO,EAAC,aAAa,eAAe,YAAY,SAAA,GACpG,iBAAO,OACZ;AAAA,YAEA,oBAAC,gBAAa,OAAO,EAAC,SAAS,UAAU,4BAAkB,YAAA,CAAY;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA,EAC3E,GArBQ,OAAO,EAuBvB;AAAA,IAER;AACA,WACI;AAAA,MAAC;AAAA,MAAA;AAAA,QAA6B,SAAQ;AAAA,QAAW,WAAU;AAAA,QAAU,cAAY,OAAO;AAAA,QACjF,OAAO,EAAC,MAAM,SAAS,UAAU,yBAAyB,GAAG,KAAK,iBAAiB,SAAS,cAAc,EAAA;AAAA,QAC7G,UAAA;AAAA,UAAA,qBAAC,oBAAA,EAAmB,WAAU,OAAM,SAAS,KAAK,OAAO,EAAC,YAAY,UAAU,IAAI,KAAK,IAAI,OACzF,UAAA;AAAA,YAAA,oBAAC,mBAAA,EAAkB,SAAQ,aAAY,WAAU,MAAK,OAAO,EAAC,UAAU,GAAG,UAAU,EAAA,GAAK,iBAAO,OAAM;AAAA,YACvG;AAAA,cAAC;AAAA,cAAA;AAAA,gBAAK,MAAK;AAAA,gBAAQ,OAAO;AAAA,gBAAO,OAAO,SAAS,YAAY;AAAA,gBACvD,OAAO,SAAS,EAAE,aAAa,EAAC,OAAO,OAAO,MAAA,CAAM,IAAI;AAAA,cAAA;AAAA,YAAA;AAAA,YAC7D,aACG,oBAAC,SAAA,EAAQ,OAAO,EAAE,WAAW,EAAC,QAAQ,OAAO,MAAA,CAAM,GAC/C,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,EAAE,WAAW,EAAC,QAAQ,OAAO,MAAA,CAAM,GAAG,SAAS,MAAM,kBAAkB,OAAO,EAAE,GACjH,UAAA,oBAAC,gBAAA,EAAe,UAAS,QAAA,CAAQ,GACrC,GACJ;AAAA,YAEJ,oBAAC,aAAA,EAAY,OAAO,EAAE,iBAAiB,EAAC,QAAQ,OAAO,MAAA,CAAM,GAAG,SAAS,gBAAgB,MAAM,EAAA,CAAG;AAAA,YACjG,OAAO,eACJ,oBAAC,SAAA,EAAQ,OAAO,EAAE,kBAAkB,EAAC,QAAQ,OAAO,MAAA,CAAM,GACtD,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBAAW,MAAK;AAAA,gBAAQ,cAAY,EAAE,kBAAkB,EAAC,QAAQ,OAAO,OAAM;AAAA,gBAAG,iBAAc;AAAA,gBACpF,SAAS,MAAM,aAAa,OAAO,EAAE;AAAA,gBAC7C,UAAA,oBAAC,oCAAA,EAAmC,UAAS,QAAA,CAAQ;AAAA,cAAA;AAAA,YAAA,EACzD,CACJ;AAAA,UAAA,GAER;AAAA,UACC,mBAAmB,OAAO,MACvB,oBAAC,kBAAe,OAAO,EAAC,IAAI,EAAA,GACxB,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cAAS,aAAa,OAAO;AAAA,cAAO;AAAA,cAAM,SAAS,MAAM,kBAAkB,IAAI;AAAA,cACtE,OAAO,CAAC,UAAU,UAAU,EAAC,UAAU,OAAO,IAAI,MAAA,CAAM;AAAA,YAAA;AAAA,UAAA,GACtE;AAAA,8BAEH,WAAA,EAAU,aAAa,OAAO,IAC1B,UAAA,CAAC,mBAAmB,sBACjB;AAAA,YAAC;AAAA,YAAA;AAAA,cAAe,KAAK,kBAAkB;AAAA,cAAW,GAAG,kBAAkB;AAAA,cAAgB,SAAS;AAAA,cACzF,OAAO;AAAA,gBACH,WAAW;AAAA,gBAAyB,cAAc;AAAA,gBAAG,YAAY;AAAA,gBACjE,SAAS,kBAAkB,iBAAiB,iBAAiB;AAAA,cAAA;AAAA,cAEnE,UAAA;AAAA,gBAAA,YAAY,IAAI,CAAC,MAAM,UACpB,oBAAC,WAAA,EAAwB,aAAa,KAAK,IAAI,OAC1C,UAAA,CAAC,mBAAmB,sBACjB;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBAAW,KAAK,kBAAkB;AAAA,oBAAW,GAAG,kBAAkB;AAAA,oBAAiB,GAAG,kBAAkB;AAAA,oBAClG,WAAW,kBAAkB,aAAa,IAAI;AAAA,oBAC9C,SAAS,cAAc,MAAM,YAAY,KAAK,EAAE,IAAI;AAAA,oBACpD,OAAO,EAAC,GAAG,KAAK,cAAc,GAAG,QAAQ,QAAQ,UAAU,YAAY,mBAAmB,EAAC,SAAS,GAAG,cAAc,iBAAc;AAAA,oBAErI,UAAA;AAAA,sBAAA,eACG,oBAAC,mBAAA,EAAkB,OAAO,EAAC,UAAU,YAAY,KAAK,GAAG,OAAO,EAAA,GAC5D,UAAA,oBAAC,aAAA,EAAY,OAAO,EAAE,eAAe,EAAC,OAAO,KAAK,MAAA,CAAM,GAAG,SAAS,YAAY,IAAI,EAAA,CAAG,EAAA,CAC3F;AAAA,sBAEJ,oBAAC,qBAAkB,OAAO,cAAc,EAAC,IAAI,EAAA,IAAK,QAC7C,UAAA,aAAa,WAAW,IAAI,wBAAK,aAAA,EAAY,MAAY,GAAM,OAAc,YAAY,gBAAgB,EAAA,CAC9G;AAAA,oBAAA;AAAA,kBAAA;AAAA,gBAAA,KAdI,KAAK,EAiBrB,CACH;AAAA,gBACA,kBAAkB;AAAA,gBAElB,YAAY,WAAW,KAAK,CAAC,kBAAkB,kBAAkB,mBAAmB,OAAO,MACxF,oBAAC,aAAA,EAAY,SAAQ,WAAU,OAAM,kBAAiB,OAAO,EAAC,WAAW,UAAU,IAAI,EAAA,GAClF,UAAA,QAAQ,IAAI,EAAE,eAAe,IAAI,EAAE,OAAO,EAAA,CAC/C;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA,EAER,CAER;AAAA,QAAA;AAAA,MAAA;AAAA,MAjEe,OAAO;AAAA,IAAA;AAAA,EAoElC,CAAC,GACL,GACJ;AAER;"}
|
|
1
|
+
{"version":3,"file":"KanbanBoard.js","sources":["../../src/kanban/KanbanBoard.jsx"],"sourcesContent":["// Un tablero kanban (@stackcraftmx/calcita/kanban): columnas con tarjetas que se arrastran de una a otra con el ratón, con el\n// dedo o con el teclado (espacio para levantar, flechas para mover, espacio para soltar). Con @hello-pangea/dnd por dentro.\n//\n// <KanbanBoard columns={[{id: 'todo', title: 'Por hacer'}, {id: 'doing', title: 'En proceso', limit: 3}]}\n// cards={cards} onMove={(move) => api.post('/tasks/move', move)} ariaLabel=\"Tablero de tareas\" />\n//\n// El tablero no guarda nada: avisa la movida (onMove) y quien lo usa actualiza sus tarjetas (moveCard de kanbanMoves.js lo\n// hace sin esperar al servidor). Una columna con tope (limit, el trabajo en curso) avisa en ámbar cuando lo pasa.\n//\n// Accesible: el lector de pantalla anuncia en el idioma de la app qué se levantó, a dónde va y dónde quedó.\n//\n// Lo de un producto, todo hacia afuera (el tablero no guarda nada ni llama a ningún servidor):\n// - la tarjeta por omisión pinta lo que traiga: etiquetas (texto o {label, color}), responsables con su monograma, la\n// fecha límite (roja si ya venció, ámbar si es hoy), el avance de su lista de pendientes, comentarios y adjuntos. Para\n// otra tarjeta: renderCard;\n// - «+» en cada columna para agregar escribiendo el título (onAddCard({columnId, title}));\n// - «⋯» en cada tarjeta y en cada columna con las acciones que dé la pantalla (cardActions, columnActions): editar,\n// asignarme, mover sin arrastrar, borrar…;\n// - terminar una tarea: con onCardComplete cada tarjeta lleva su círculo para marcarla (y desmarcarla); qué pasa al\n// terminarla (pasarla a «Listo», guardar la fecha) lo decide la pantalla. Una tarjeta con completed: true sale con su\n// palomita verde y su fecha límite ya no se pinta de rojo;\n// - columnas que se encogen (collapsible), como el congelador (icebox) de ideas para después: se quedan en una tira angosta\n// con su nombre y su cuenta, y se les puede soltar una tarjeta igual. defaultCollapsed la abre encogida y\n// onColumnCollapse avisa cada cambio (para guardarlo si la pantalla quiere);\n// - texts para cualquier texto (la forma de `kanban` en los textos del paquete).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaKanbanBoard con defaultProps y\n// styleOverrides de sus partes: completeButton, tags, titleRow, doneIcon, cardTitle, meta, due, dueIcon, dueText,\n// checklist, checklistIcon, comments, commentsIcon, attachments, attachmentsIcon, assignees, root, collapsedColumn,\n// collapsedTitle, hidden, column, columnHeader, columnTitle, quickAdd, dropZone, card, cardActions, cardContent, empty.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {DragDropContext, Draggable, Droppable} from '@hello-pangea/dnd';\nimport Avatar from '@mui/material/Avatar';\nimport AvatarGroup from '@mui/material/AvatarGroup';\nimport Box from '@mui/material/Box';\nimport Chip from '@mui/material/Chip';\nimport IconButton from '@mui/material/IconButton';\nimport Paper from '@mui/material/Paper';\nimport Stack from '@mui/material/Stack';\nimport TextField from '@mui/material/TextField';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport AttachFileRoundedIcon from '@mui/icons-material/AttachFileRounded';\nimport ChatBubbleOutlineRoundedIcon from '@mui/icons-material/ChatBubbleOutlineRounded';\nimport CheckBoxOutlinedIcon from '@mui/icons-material/CheckBoxOutlined';\nimport CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';\nimport EventOutlinedIcon from '@mui/icons-material/EventOutlined';\nimport KeyboardDoubleArrowLeftRoundedIcon from '@mui/icons-material/KeyboardDoubleArrowLeftRounded';\nimport KeyboardDoubleArrowRightRoundedIcon from '@mui/icons-material/KeyboardDoubleArrowRightRounded';\nimport RadioButtonUncheckedRoundedIcon from '@mui/icons-material/RadioButtonUncheckedRounded';\n\nimport {initialsOf} from '@/appShell/menuItemText.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {DUE_STATUS, dueStatusOf, tagOf, todayIsoDateOf} from '@/kanban/kanbanCards.js';\nimport {cardsOfColumn, isOverLimit} from '@/kanban/kanbanMoves.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport ActionsMenu from '@/ui/actions/ActionsMenu.jsx';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaKanbanBoard)\nconst THEME_NAME = 'CalcitaKanbanBoard';\nconst KanbanCompleteButton = piecePart(THEME_NAME, 'CompleteButton', IconButton);\nconst KanbanTags = piecePart(THEME_NAME, 'Tags', Stack);\nconst KanbanTitleRow = piecePart(THEME_NAME, 'TitleRow', Stack);\nconst KanbanDoneIcon = piecePart(THEME_NAME, 'DoneIcon', CheckCircleRoundedIcon);\nconst KanbanCardTitle = piecePart(THEME_NAME, 'CardTitle', Typography);\nconst KanbanMeta = piecePart(THEME_NAME, 'Meta', Stack);\nconst KanbanDue = piecePart(THEME_NAME, 'Due', Stack);\nconst KanbanDueIcon = piecePart(THEME_NAME, 'DueIcon', EventOutlinedIcon);\nconst KanbanDueText = piecePart(THEME_NAME, 'DueText', Typography);\nconst KanbanChecklist = piecePart(THEME_NAME, 'Checklist', Stack);\nconst KanbanChecklistIcon = piecePart(THEME_NAME, 'ChecklistIcon', CheckBoxOutlinedIcon);\nconst KanbanComments = piecePart(THEME_NAME, 'Comments', Stack);\nconst KanbanCommentsIcon = piecePart(THEME_NAME, 'CommentsIcon', ChatBubbleOutlineRoundedIcon);\nconst KanbanAttachments = piecePart(THEME_NAME, 'Attachments', Stack);\nconst KanbanAttachmentsIcon = piecePart(THEME_NAME, 'AttachmentsIcon', AttachFileRoundedIcon);\nconst KanbanAssignees = piecePart(THEME_NAME, 'Assignees', AvatarGroup);\nconst KanbanRoot = piecePart(THEME_NAME, 'Root', Box);\nconst KanbanCollapsedColumn = piecePart(THEME_NAME, 'CollapsedColumn', Paper);\nconst KanbanCollapsedTitle = piecePart(THEME_NAME, 'CollapsedTitle', Typography);\nconst KanbanHidden = piecePart(THEME_NAME, 'Hidden', Box);\nconst KanbanColumn = piecePart(THEME_NAME, 'Column', Paper);\nconst KanbanColumnHeader = piecePart(THEME_NAME, 'ColumnHeader', Stack);\nconst KanbanColumnTitle = piecePart(THEME_NAME, 'ColumnTitle', Typography);\nconst KanbanQuickAdd = piecePart(THEME_NAME, 'QuickAdd', Box);\nconst KanbanDropZone = piecePart(THEME_NAME, 'DropZone', Stack);\nconst KanbanCard = piecePart(THEME_NAME, 'Card', Paper);\nconst KanbanCardActions = piecePart(THEME_NAME, 'CardActions', Box);\nconst KanbanCardContent = piecePart(THEME_NAME, 'CardContent', Box);\nconst KanbanEmpty = piecePart(THEME_NAME, 'Empty', Typography);\n\n// las columnas se reparten el ancho, sin bajar de este mínimo; si no caben, el tablero desliza de lado.\n// OJO: con el teclado, la librería solo mueve una tarjeta a columnas que se ven COMPLETAS: con columnas fijas de 300 px la\n// cuarta quedaba cortada en un escritorio y no se podía soltar ahí con el teclado\nconst COLUMN_MIN_WIDTH_PIXELS = 220;\n// lo alto que mide una columna vacía: lo justo para soltar ahí una tarjeta\nconst EMPTY_COLUMN_MIN_HEIGHT = 96;\n// cuántas caras de responsables se ven antes del «+2»\nconst MAX_VISIBLE_ASSIGNEES = 3;\nconst ASSIGNEE_AVATAR_PIXELS = 24;\n// lo ancho de una columna encogida: su nombre de lado, su cuenta y su botón para abrirla\nconst COLLAPSED_COLUMN_WIDTH_PIXELS = 52;\n// el color del texto de la fecha según cómo va\nconst DUE_COLOR = Object.freeze({[DUE_STATUS.overdue]: 'error.main', [DUE_STATUS.today]: 'warning.dark', [DUE_STATUS.upcoming]: 'text.secondary'});\n\n/**\n * El círculo para terminar una tarea (o reabrirla, si ya está terminada). El clic no levanta la tarjeta ni la abre.\n *\n * @param {{card: any, t: Function, onComplete: (card: any, completed: boolean) => void}} props\n */\nfunction CompleteButton({card, t, onComplete}) {\n const label = card.completed ? t('reopen', {title: card.title}) : t('complete', {title: card.title});\n return (\n <Tooltip title={label}>\n <KanbanCompleteButton size=\"small\" aria-label={label} aria-pressed={Boolean(card.completed)} ownSx={{m: -0.5, color: card.completed ? 'success.main' : 'text.secondary'}}\n onClick={(event) => {\n event.stopPropagation();\n onComplete(card, !card.completed);\n }}>\n {card.completed ? <CheckCircleRoundedIcon fontSize=\"small\" /> : <RadioButtonUncheckedRoundedIcon fontSize=\"small\" />}\n </KanbanCompleteButton>\n </Tooltip>\n );\n}\n\n/**\n * Una tarjeta por omisión: lo que traiga, sin pedir nada (título, descripción, etiquetas, responsables, fecha, pendientes,\n * comentarios y adjuntos).\n *\n * @param {{card: any, t: Function, today: string, onComplete?: (card: any, completed: boolean) => void}} props\n */\nfunction DefaultCard({card, t, today, onComplete}) {\n const {formatDate} = useFormatters();\n // terminada, su fecha ya no apura: se pinta tenue, sin rojo ni ámbar\n const dueStatus = card.completed && card.dueDate ? DUE_STATUS.upcoming : dueStatusOf(card.dueDate, today);\n let dueText = '';\n if (card.completed && card.dueDate) {\n dueText = t('dueCompleted', {date: formatDate(card.dueDate)});\n } else if (dueStatus === DUE_STATUS.overdue) {\n dueText = t('dueOverdue', {date: formatDate(card.dueDate)});\n } else if (dueStatus === DUE_STATUS.today) {\n dueText = t('dueToday');\n } else if (dueStatus) {\n dueText = t('dueOn', {date: formatDate(card.dueDate)});\n }\n const hasFooter = dueStatus || card.checklist?.total > 0 || card.comments > 0 || card.attachments > 0 || card.assignees?.length > 0;\n\n return (\n <Stack spacing={0.75}>\n {card.tags?.length > 0 && (\n <KanbanTags direction=\"row\" spacing={0.5} useFlexGap ownSx={{flexWrap: 'wrap'}}>\n {card.tags.map((rawTag) => {\n const tag = tagOf(rawTag);\n return <Chip key={tag.label} label={tag.label} size=\"small\" color={tag.color} variant={tag.color === 'default' ? 'filled' : 'outlined'} />;\n })}\n </KanbanTags>\n )}\n <KanbanTitleRow direction=\"row\" spacing={1} ownSx={{alignItems: 'flex-start'}}>\n {onComplete && <CompleteButton card={card} t={t} onComplete={onComplete} />}\n {!onComplete && card.completed && (\n <KanbanDoneIcon fontSize=\"small\" ownSx={{color: 'success.main'}} titleAccess={t('completed')} />\n )}\n <KanbanCardTitle variant=\"subtitle2\" component=\"p\" ownSx={{color: card.completed ? 'text.secondary' : undefined}}>{card.title}</KanbanCardTitle>\n </KanbanTitleRow>\n {card.description && <Typography variant=\"body2\" color=\"text.secondary\">{card.description}</Typography>}\n {hasFooter && (\n <KanbanMeta direction=\"row\" spacing={1.25} useFlexGap ownSx={{alignItems: 'center', flexWrap: 'wrap', color: 'text.secondary'}}>\n {dueStatus && (\n <KanbanDue direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center', color: DUE_COLOR[dueStatus]}}>\n <KanbanDueIcon ownSx={{fontSize: 16}} />\n <KanbanDueText variant=\"caption\" ownSx={{fontWeight: dueStatus === DUE_STATUS.upcoming ? 400 : 600}}>{dueText}</KanbanDueText>\n </KanbanDue>\n )}\n {card.checklist?.total > 0 && (\n <KanbanChecklist direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center'}}\n aria-label={t('checklist', {done: card.checklist.done, total: card.checklist.total})}>\n <KanbanChecklistIcon ownSx={{fontSize: 16}} />\n <Typography variant=\"caption\" aria-hidden=\"true\">{card.checklist.done}/{card.checklist.total}</Typography>\n </KanbanChecklist>\n )}\n {card.comments > 0 && (\n <KanbanComments direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center'}} aria-label={t('comments', {count: card.comments})}>\n <KanbanCommentsIcon ownSx={{fontSize: 16}} />\n <Typography variant=\"caption\" aria-hidden=\"true\">{card.comments}</Typography>\n </KanbanComments>\n )}\n {card.attachments > 0 && (\n <KanbanAttachments direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center'}} aria-label={t('attachments', {count: card.attachments})}>\n <KanbanAttachmentsIcon ownSx={{fontSize: 16}} />\n <Typography variant=\"caption\" aria-hidden=\"true\">{card.attachments}</Typography>\n </KanbanAttachments>\n )}\n {card.assignees?.length > 0 && (\n <KanbanAssignees max={MAX_VISIBLE_ASSIGNEES} ownSx={{ml: 'auto', '& .MuiAvatar-root': {width: ASSIGNEE_AVATAR_PIXELS, height: ASSIGNEE_AVATAR_PIXELS, fontSize: 11}}}\n aria-label={t('assignees', {names: card.assignees.map((person) => person.name).join(', ')})}>\n {card.assignees.map((person) => (\n <Tooltip key={person.id} title={person.name}>\n <Avatar alt={person.name} src={person.avatarUrl}>{initialsOf(person.name)}</Avatar>\n </Tooltip>\n ))}\n </KanbanAssignees>\n )}\n </KanbanMeta>\n )}\n </Stack>\n );\n}\n\n/**\n * Agregar una tarjeta escribiendo su título: Enter la agrega (y deja el campo listo para otra), Escape cierra.\n *\n * @param {{columnTitle: string, onAdd: (title: string) => void, onClose: () => void, t: Function}} props\n */\nfunction QuickAdd({columnTitle, onAdd, onClose, t}) {\n const [title, setTitle] = useState('');\n return (\n <TextField size=\"small\" fullWidth autoFocus multiline maxRows={4} value={title} placeholder={t('addCardPlaceholder')}\n slotProps={{htmlInput: {'aria-label': t('addCard', {column: columnTitle})}}}\n onChange={(event) => setTitle(event.target.value)}\n onBlur={() => !title.trim() && onClose()}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n onClose();\n }\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n if (title.trim()) {\n onAdd(title.trim());\n setTitle('');\n }\n }\n }} />\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, title: string, limit?: number, count?: number, collapsible?: boolean, defaultCollapsed?: boolean}>} props.columns\n * en su orden. count: cuántas tarjetas tiene de verdad cuando la pantalla le pasa solo las filtradas (el tope se cuenta\n * contra todas). collapsible: lleva su botón para encogerse (el congelador); defaultCollapsed: empieza encogida\n * @param {Array<{id: string, columnId: string, title: string, completed?: boolean, description?: string, tags?: Array<string|{label: string, color?: string}>, assignees?: Array<{id: string, name: string, avatarUrl?: string}>, dueDate?: string, checklist?: {done: number, total: number}, comments?: number, attachments?: number} & Record<string, any>>} props.cards\n * el orden de cada columna es el orden en este arreglo; cada tarjeta puede traer sus campos propios (llegan a renderCard,\n * a cardActions y a onCardClick tal cual). dueDate: AAAA-MM-DD\n * @param {(move: {cardId: string, fromColumnId: string, toColumnId: string, toIndex: number}) => void} props.onMove\n * @param {(card: any) => import('react').ReactNode} [props.renderCard] lo de adentro de cada tarjeta (recibe la tarjeta de cards)\n * @param {(cardId: string) => void} [props.onCardClick]\n * @param {(newCard: {columnId: string, title: string}) => void} [props.onAddCard] con él cada columna lleva su «+» para agregar\n * escribiendo el título\n * @param {(card: any) => Array<{key: string, label: string, icon?: import('react').ReactNode, color?: string, disabled?: boolean, onClick: () => void}>} [props.cardActions]\n * las acciones del «⋯» de cada tarjeta (editar, asignarme, borrar…); sin ellas no hay «⋯»\n * @param {(column: any) => Array<{key: string, label: string, icon?: import('react').ReactNode, color?: string, disabled?: boolean, onClick: () => void}>} [props.columnActions]\n * las acciones del «⋯» de cada columna (renombrar, limpiar…)\n * @param {(card: any, completed: boolean) => void} [props.onCardComplete] con él cada tarjeta lleva su círculo para terminarla\n * (completed true) o reabrirla (false); la pantalla decide qué más pasa (moverla a «Listo», guardar la fecha)\n * @param {(columnId: string, collapsed: boolean) => void} [props.onColumnCollapse] cada que una columna se encoge o se abre\n * @param {string} props.ariaLabel ya traducido\n * @param {object} [props.texts] los textos que cambian, con la forma de `kanban` en los textos del paquete\n */\nexport default function KanbanBoard(props) {\n // lo que el proyecto puso en su tema (components.CalcitaKanbanBoard.defaultProps) y encima lo que llega aquí\n const {\n columns, cards, onMove, renderCard, onCardClick, onAddCard, cardActions, columnActions, onCardComplete, onColumnCollapse, ariaLabel, texts,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('kanban', texts);\n // la columna donde se está escribiendo una tarjeta nueva\n const [addingColumnId, setAddingColumnId] = useState(null);\n // las columnas encogidas: empiezan como dice defaultCollapsed y luego las mueve la persona\n const [collapsedIds, setCollapsedIds] = useState(() => new Set(columns.filter((column) => column.defaultCollapsed).map((column) => column.id)));\n const toggleColumn = (columnId) => {\n const collapsed = !collapsedIds.has(columnId);\n setCollapsedIds((current) => {\n const next = new Set(current);\n if (collapsed) {\n next.add(columnId);\n } else {\n next.delete(columnId);\n }\n return next;\n });\n onColumnCollapse?.(columnId, collapsed);\n };\n const today = todayIsoDateOf();\n const columnTitleOf = (columnId) => columns.find((column) => column.id === columnId)?.title ?? columnId;\n const cardTitleOf = (cardId) => cards.find((card) => card.id === cardId)?.title ?? cardId;\n\n // lo que oye quien usa lector de pantalla (posiciones desde 1, como se cuentan)\n const announceStart = (start, provided) => provided.announce(t('lifted', {\n title: cardTitleOf(start.draggableId), column: columnTitleOf(start.source.droppableId), position: start.source.index + 1,\n }));\n const announceUpdate = (update, provided) => {\n if (update.destination) {\n provided.announce(t('moving', {column: columnTitleOf(update.destination.droppableId), position: update.destination.index + 1}));\n }\n };\n const finishMove = (result, provided) => {\n const {draggableId, source, destination} = result;\n // se soltó fuera de una columna o en el mismo lugar: nada que mover\n if (!destination || (destination.droppableId === source.droppableId && destination.index === source.index)) {\n provided.announce(t('cancelled', {title: cardTitleOf(draggableId)}));\n return;\n }\n provided.announce(t('dropped', {title: cardTitleOf(draggableId), column: columnTitleOf(destination.droppableId), position: destination.index + 1}));\n onMove({cardId: draggableId, fromColumnId: source.droppableId, toColumnId: destination.droppableId, toIndex: destination.index});\n };\n\n return (\n <DragDropContext onDragStart={announceStart} onDragUpdate={announceUpdate} onDragEnd={finishMove} dragHandleUsageInstructions={t('instructions')}>\n <KanbanRoot role=\"region\" aria-label={ariaLabel} ownSx={{\n display: 'flex', gap: 2, overflowX: 'auto', pb: 1, scrollSnapType: 'x proximity', alignItems: 'flex-start',\n }}>\n {columns.map((column) => {\n const columnCards = cardsOfColumn(cards, column.id);\n // con un filtro puesto la pantalla dice cuántas tiene de verdad: «1 / 3» mentía con la columna llena\n const total = Number.isInteger(column.count) ? column.count : columnCards.length;\n const isOver = isOverLimit(column, total);\n const count = Number.isInteger(column.limit) ? `${total} / ${column.limit}` : String(total);\n const isCollapsed = column.collapsible && collapsedIds.has(column.id);\n // encogida: una tira con su nombre de lado y su cuenta; se le puede soltar una tarjeta (queda arriba)\n if (isCollapsed) {\n return (\n <Droppable key={column.id} droppableId={column.id}>\n {(droppableProvided, droppableSnapshot) => (\n <KanbanCollapsedColumn ref={droppableProvided.innerRef} {...droppableProvided.droppableProps} variant=\"outlined\" component=\"section\"\n aria-label={column.title}\n ownSx={{\n flex: '0 0 auto', width: COLLAPSED_COLUMN_WIDTH_PIXELS, py: 1, borderRadius: 3, display: 'flex',\n flexDirection: 'column', alignItems: 'center', gap: 1, minHeight: EMPTY_COLUMN_MIN_HEIGHT * 2,\n bgcolor: droppableSnapshot.isDraggingOver ? 'action.hover' : undefined,\n }}>\n <Tooltip title={t('expandColumn', {column: column.title})} placement=\"right\">\n <IconButton size=\"small\" aria-label={t('expandColumn', {column: column.title})} aria-expanded=\"false\"\n onClick={() => toggleColumn(column.id)}>\n <KeyboardDoubleArrowRightRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Chip size=\"small\" label={count} color={isOver ? 'warning' : 'default'} />\n <KanbanCollapsedTitle variant=\"subtitle2\" component=\"h3\" ownSx={{writingMode: 'vertical-rl', whiteSpace: 'nowrap'}}>\n {column.title}\n </KanbanCollapsedTitle>\n {/* el lugar de la tarjeta que se arrastra; encogida no la enseña */}\n <KanbanHidden ownSx={{display: 'none'}}>{droppableProvided.placeholder}</KanbanHidden>\n </KanbanCollapsedColumn>\n )}\n </Droppable>\n );\n }\n return (\n <KanbanColumn key={column.id} variant=\"outlined\" component=\"section\" aria-label={column.title}\n ownSx={{flex: '1 1 0', minWidth: COLUMN_MIN_WIDTH_PIXELS, p: 1.5, scrollSnapAlign: 'start', borderRadius: 3}}>\n <KanbanColumnHeader direction=\"row\" spacing={0.5} ownSx={{alignItems: 'center', mb: 1.5, px: 0.5}}>\n <KanbanColumnTitle variant=\"subtitle2\" component=\"h3\" ownSx={{flexGrow: 1, minWidth: 0}}>{column.title}</KanbanColumnTitle>\n <Chip size=\"small\" label={count} color={isOver ? 'warning' : 'default'}\n title={isOver ? t('overLimit', {limit: column.limit}) : undefined} />\n {onAddCard && (\n <Tooltip title={t('addCard', {column: column.title})}>\n <IconButton size=\"small\" aria-label={t('addCard', {column: column.title})} onClick={() => setAddingColumnId(column.id)}>\n <AddRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <ActionsMenu label={t('columnActions', {column: column.title})} actions={columnActions?.(column)} />\n {column.collapsible && (\n <Tooltip title={t('collapseColumn', {column: column.title})}>\n <IconButton size=\"small\" aria-label={t('collapseColumn', {column: column.title})} aria-expanded=\"true\"\n onClick={() => toggleColumn(column.id)}>\n <KeyboardDoubleArrowLeftRoundedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n </KanbanColumnHeader>\n {addingColumnId === column.id && (\n <KanbanQuickAdd ownSx={{mb: 1}}>\n <QuickAdd columnTitle={column.title} t={t} onClose={() => setAddingColumnId(null)}\n onAdd={(title) => onAddCard({columnId: column.id, title})} />\n </KanbanQuickAdd>\n )}\n <Droppable droppableId={column.id}>\n {(droppableProvided, droppableSnapshot) => (\n <KanbanDropZone ref={droppableProvided.innerRef} {...droppableProvided.droppableProps} spacing={1}\n ownSx={{\n minHeight: EMPTY_COLUMN_MIN_HEIGHT, borderRadius: 2, transition: 'background-color 160ms',\n bgcolor: droppableSnapshot.isDraggingOver ? 'action.hover' : 'transparent',\n }}>\n {columnCards.map((card, index) => (\n <Draggable key={card.id} draggableId={card.id} index={index}>\n {(draggableProvided, draggableSnapshot) => (\n <KanbanCard ref={draggableProvided.innerRef} {...draggableProvided.draggableProps} {...draggableProvided.dragHandleProps}\n elevation={draggableSnapshot.isDragging ? 8 : 1}\n onClick={onCardClick ? () => onCardClick(card.id) : undefined}\n ownSx={{p: 1.5, borderRadius: 2, cursor: 'grab', position: 'relative', '&:focus-visible': {outline: 2, outlineColor: 'primary.main'}}}>\n {/* el «⋯» arriba a la derecha; el contenido le deja su lugar */}\n {cardActions && (\n <KanbanCardActions ownSx={{position: 'absolute', top: 4, right: 4}}>\n <ActionsMenu label={t('cardActions', {title: card.title})} actions={cardActions(card)} />\n </KanbanCardActions>\n )}\n <KanbanCardContent ownSx={cardActions ? {pr: 3} : undefined}>\n {renderCard ? renderCard(card) : <DefaultCard card={card} t={t} today={today} onComplete={onCardComplete} />}\n </KanbanCardContent>\n </KanbanCard>\n )}\n </Draggable>\n ))}\n {droppableProvided.placeholder}\n {/* vacía de verdad, o con tarjetas que el filtro de la pantalla esconde (no se dice «sin tarjetas») */}\n {columnCards.length === 0 && !droppableSnapshot.isDraggingOver && addingColumnId !== column.id && (\n <KanbanEmpty variant=\"caption\" color=\"text.secondary\" ownSx={{textAlign: 'center', py: 3}}>\n {total > 0 ? t('emptyFiltered') : t('empty')}\n </KanbanEmpty>\n )}\n </KanbanDropZone>\n )}\n </Droppable>\n </KanbanColumn>\n );\n })}\n </KanbanRoot>\n </DragDropContext>\n );\n}\n"],"names":[],"mappings":"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"}
|
package/dist/maps/MapView.js
CHANGED
|
@@ -10,6 +10,7 @@ import { useTheme } from "@mui/material/styles";
|
|
|
10
10
|
import { boundsOfMarkers, MAP_PIN_SIZE, mapPinHtmlOf } from "./mapMarkers.js";
|
|
11
11
|
import { paletteOf } from "../theme/components/paletteOf.js";
|
|
12
12
|
import { piecePart, usePieceProps } from "../theme/themePieces.js";
|
|
13
|
+
import { warnUnknownProps } from "../toolkit/unknownProps.js";
|
|
13
14
|
const THEME_NAME = "CalcitaMapView";
|
|
14
15
|
const MapPopupText = piecePart(THEME_NAME, "PopupText", Typography);
|
|
15
16
|
const MapRoot = piecePart(THEME_NAME, "Root", Box);
|
|
@@ -65,8 +66,10 @@ function MapView(props) {
|
|
|
65
66
|
renderPopup,
|
|
66
67
|
onMapClick,
|
|
67
68
|
children,
|
|
68
|
-
mapProps
|
|
69
|
+
mapProps,
|
|
70
|
+
...unknownProps
|
|
69
71
|
} = usePieceProps(props, THEME_NAME);
|
|
72
|
+
warnUnknownProps(THEME_NAME, unknownProps);
|
|
70
73
|
const theme = useTheme();
|
|
71
74
|
const palette = paletteOf(theme);
|
|
72
75
|
const markerRefs = useRef(/* @__PURE__ */ new Map());
|
package/dist/maps/MapView.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapView.js","sources":["../../src/maps/MapView.jsx"],"sourcesContent":["// Un mapa con marcadores (@stackcraftmx/calcita/maps): Leaflet con los mosaicos de OpenStreetMap. Cada marcador es un\n// alfiler del color del tema (mapMarkers.js); el elegido va más grande, el mapa vuela a él y abre su globo. Al cambiar los\n// marcadores, el mapa se acomoda para que se vean todos.\n//\n// <MapView markers={[{id: 'gdl', position: [20.67, -103.35], title: 'Sucursal Guadalajara', tone: 'success'}]}\n// selectedId={selectedId} onSelect={setSelectedId} ariaLabel=\"Mapa de sucursales\" />\n//\n// En oscuro los mosaicos se oscurecen con un filtro (OpenStreetMap solo los da claros). Los mosaicos públicos de\n// OpenStreetMap son para poco tráfico y piden su crédito: para mucho uso, un proveedor propio en tileLayer.\n//\n// Se extiende sin tocar el framework: cualquier contenido en el globo (renderPopup: un estado, un botón, una foto, «Cómo\n// llegar»), capas propias de react-leaflet adentro (children: zonas de entrega, un radio), el clic en el mapa (onMapClick:\n// agregar un punto) y lo demás de MapContainer de Leaflet (mapProps).\n//\n// OJO, Next.js: Leaflet usa window al cargarse; en Next se importa con next/dynamic y ssr: false.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaMapView con defaultProps y\n// styleOverrides de sus partes: popupText, root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport 'leaflet/dist/leaflet.css';\nimport {useEffect, useMemo, useRef} from 'react';\nimport L from 'leaflet';\nimport {MapContainer, Marker, Popup, TileLayer, useMap, useMapEvents} from 'react-leaflet';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\n\nimport {MAP_PIN_SIZE, boundsOfMarkers, mapPinHtmlOf} from '@/maps/mapMarkers.js';\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaMapView)\nconst THEME_NAME = 'CalcitaMapView';\nconst MapPopupText = piecePart(THEME_NAME, 'PopupText', Typography);\nconst MapRoot = piecePart(THEME_NAME, 'Root', Box);\n\n/** Los mosaicos de OpenStreetMap con su crédito (lo pide su política de uso). */\nexport const OPEN_STREET_MAP_TILES = Object.freeze({\n url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>',\n});\n\n// México completo cuando no hay marcadores\nconst DEFAULT_CENTER = Object.freeze([23.6, -102.5]);\nconst DEFAULT_ZOOM = 5;\n// al volar a un marcador elegido, al menos este acercamiento\nconst SELECTED_ZOOM = 13;\n// al acomodar el mapa a los marcadores: el margen (px) y que no se acerque de más con uno solo\nconst FIT_PADDING_PIXELS = 48;\nconst FIT_MAX_ZOOM = 13;\nconst DEFAULT_HEIGHT_PIXELS = 420;\n\n/**\n * Acomoda el mapa para que se vean todos los marcadores cada vez que cambian.\n *\n * @param {{bounds: [[number, number], [number, number]]|null}} props\n */\nfunction FitToMarkers({bounds}) {\n const map = useMap();\n const boundsKey = JSON.stringify(bounds);\n useEffect(() => {\n if (bounds) {\n map.fitBounds(bounds, {padding: [FIT_PADDING_PIXELS, FIT_PADDING_PIXELS], maxZoom: FIT_MAX_ZOOM});\n }\n // OJO: depende del texto del recuadro y no del arreglo: el arreglo es nuevo en cada pintada y el mapa se movería solo\n }, [map, boundsKey]);\n return null;\n}\n\n/**\n * Vuela al marcador elegido y abre su globo.\n *\n * @param {{selectedMarker: object|undefined, markerRefs: object}} props\n */\nfunction FlyToSelected({selectedMarker, markerRefs}) {\n const map = useMap();\n useEffect(() => {\n if (!selectedMarker) {\n return;\n }\n map.flyTo(selectedMarker.position, Math.max(map.getZoom(), SELECTED_ZOOM));\n markerRefs.current.get(selectedMarker.id)?.openPopup();\n }, [map, selectedMarker, markerRefs]);\n return null;\n}\n\n/**\n * Avisa cada clic en el mapa (fuera de un marcador) con su posición.\n *\n * @param {{onMapClick: (position: [number, number]) => void}} props\n */\nfunction MapClicks({onMapClick}) {\n useMapEvents({click: (event) => onMapClick([event.latlng.lat, event.latlng.lng])});\n return null;\n}\n\n/**\n * Lo que dice el globo de un marcador cuando la pantalla no da el suyo: su título y su descripción.\n *\n * @param {{marker: {title: string, description?: string}}} props\n */\nfunction DefaultPopupContent({marker}) {\n return (\n <>\n <Typography variant=\"subtitle2\" component=\"p\">{marker.title}</Typography>\n {marker.description && <MapPopupText variant=\"body2\" color=\"text.secondary\" component=\"p\" ownSx={{m: 0}}>{marker.description}</MapPopupText>}\n </>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, position: [number, number], title: string, description?: string, tone?: string} & Record<string, any>>} props.markers\n * tone: un color del tema (primary, success, warning, error, info…); por omisión primary\n * @param {string|null} [props.selectedId]\n * @param {(markerId: string) => void} [props.onSelect] al picar un marcador\n * @param {string} props.ariaLabel ya traducido: qué enseña el mapa\n * @param {number} [props.height] px\n * @param {{url: string, attribution: string}} [props.tileLayer] por omisión OpenStreetMap\n * @param {(marker: any) => import('react').ReactNode} [props.renderPopup] lo que dice el globo de un marcador (recibe el\n * marcador tal cual, con sus campos propios); por omisión su título y su descripción\n * @param {(position: [number, number]) => void} [props.onMapClick] al picar el mapa fuera de un marcador\n * @param {import('react').ReactNode} [props.children] capas propias de react-leaflet (Circle, Polygon, GeoJSON…)\n * @param {object} [props.mapProps] lo demás de MapContainer de react-leaflet (scrollWheelZoom, minZoom…)\n */\nexport default function MapView(props) {\n // lo que el proyecto puso en su tema (components.CalcitaMapView.defaultProps) y encima lo que llega aquí\n const {\n markers, selectedId = null, onSelect, ariaLabel, height = DEFAULT_HEIGHT_PIXELS, tileLayer = OPEN_STREET_MAP_TILES, renderPopup, onMapClick,\n children, mapProps,\n} = usePieceProps(props, THEME_NAME);\n const theme = useTheme();\n const palette = paletteOf(theme);\n const markerRefs = useRef(new Map());\n const bounds = useMemo(() => boundsOfMarkers(markers), [markers]);\n const selectedMarker = markers.find((marker) => marker.id === selectedId);\n\n // el ícono de cada marcador: se arma de nuevo solo si cambia su color o si se elige\n const iconOf = (marker) => {\n const isSelected = marker.id === selectedId;\n const size = isSelected ? MAP_PIN_SIZE.selected : MAP_PIN_SIZE.normal;\n const color = (palette[marker.tone] ?? palette.primary).main;\n return L.divIcon({html: mapPinHtmlOf(color, isSelected), className: '', iconSize: [size, size], iconAnchor: [size / 2, size], popupAnchor: [0, -size]});\n };\n\n return (\n <MapRoot role=\"region\" aria-label={ariaLabel} ownSx={(currentTheme) => ({\n height, borderRadius: 3, overflow: 'hidden', position: 'relative', zIndex: 0,\n '& .leaflet-container': {height: '100%', width: '100%', fontFamily: 'inherit', bgcolor: 'action.hover'},\n '& .leaflet-bar': {border: 0, borderRadius: '10px', overflow: 'hidden', boxShadow: currentTheme.shadows[2]},\n '& .leaflet-bar a': {bgcolor: 'background.paper', color: 'text.primary', borderBottomColor: 'divider'},\n '& .leaflet-popup-content-wrapper, & .leaflet-popup-tip': {bgcolor: 'background.paper', color: 'text.primary'},\n '& .leaflet-popup-content-wrapper': {borderRadius: '12px'},\n '& .leaflet-control-attribution': {bgcolor: 'background.paper', color: 'text.secondary', '& a': {color: 'inherit'}},\n // OpenStreetMap solo da mosaicos claros: en oscuro se invierten y se regresa el tono (el agua sigue azul)\n ...currentTheme.applyStyles('dark', {'& .leaflet-tile-pane': {filter: 'invert(1) hue-rotate(180deg) brightness(0.95) contrast(0.9)'}}),\n })}>\n <MapContainer center={markers[0]?.position ?? DEFAULT_CENTER} zoom={DEFAULT_ZOOM} scrollWheelZoom={false} {...mapProps}>\n <TileLayer url={tileLayer.url} attribution={tileLayer.attribution} />\n {onMapClick && <MapClicks onMapClick={onMapClick} />}\n {children}\n <FitToMarkers bounds={bounds} />\n <FlyToSelected selectedMarker={selectedMarker} markerRefs={markerRefs} />\n {markers.map((marker) => (\n <Marker key={marker.id} position={marker.position} icon={iconOf(marker)} title={marker.title} alt={marker.title}\n zIndexOffset={marker.id === selectedId ? 1000 : 0}\n ref={(leafletMarker) => (leafletMarker ? markerRefs.current.set(marker.id, leafletMarker) : markerRefs.current.delete(marker.id))}\n eventHandlers={{click: () => onSelect?.(marker.id)}}>\n <Popup>\n {renderPopup ? renderPopup(marker) : <DefaultPopupContent marker={marker} />}\n </Popup>\n </Marker>\n ))}\n </MapContainer>\n </MapRoot>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MapView.js","sources":["../../src/maps/MapView.jsx"],"sourcesContent":["// Un mapa con marcadores (@stackcraftmx/calcita/maps): Leaflet con los mosaicos de OpenStreetMap. Cada marcador es un\n// alfiler del color del tema (mapMarkers.js); el elegido va más grande, el mapa vuela a él y abre su globo. Al cambiar los\n// marcadores, el mapa se acomoda para que se vean todos.\n//\n// <MapView markers={[{id: 'gdl', position: [20.67, -103.35], title: 'Sucursal Guadalajara', tone: 'success'}]}\n// selectedId={selectedId} onSelect={setSelectedId} ariaLabel=\"Mapa de sucursales\" />\n//\n// En oscuro los mosaicos se oscurecen con un filtro (OpenStreetMap solo los da claros). Los mosaicos públicos de\n// OpenStreetMap son para poco tráfico y piden su crédito: para mucho uso, un proveedor propio en tileLayer.\n//\n// Se extiende sin tocar el framework: cualquier contenido en el globo (renderPopup: un estado, un botón, una foto, «Cómo\n// llegar»), capas propias de react-leaflet adentro (children: zonas de entrega, un radio), el clic en el mapa (onMapClick:\n// agregar un punto) y lo demás de MapContainer de Leaflet (mapProps).\n//\n// OJO, Next.js: Leaflet usa window al cargarse; en Next se importa con next/dynamic y ssr: false.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaMapView con defaultProps y\n// styleOverrides de sus partes: popupText, root.\n//\n// Genérico del paquete: function (regla de la casa).\nimport 'leaflet/dist/leaflet.css';\nimport {useEffect, useMemo, useRef} from 'react';\nimport L from 'leaflet';\nimport {MapContainer, Marker, Popup, TileLayer, useMap, useMapEvents} from 'react-leaflet';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\n\nimport {MAP_PIN_SIZE, boundsOfMarkers, mapPinHtmlOf} from '@/maps/mapMarkers.js';\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaMapView)\nconst THEME_NAME = 'CalcitaMapView';\nconst MapPopupText = piecePart(THEME_NAME, 'PopupText', Typography);\nconst MapRoot = piecePart(THEME_NAME, 'Root', Box);\n\n/** Los mosaicos de OpenStreetMap con su crédito (lo pide su política de uso). */\nexport const OPEN_STREET_MAP_TILES = Object.freeze({\n url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a>',\n});\n\n// México completo cuando no hay marcadores\nconst DEFAULT_CENTER = Object.freeze([23.6, -102.5]);\nconst DEFAULT_ZOOM = 5;\n// al volar a un marcador elegido, al menos este acercamiento\nconst SELECTED_ZOOM = 13;\n// al acomodar el mapa a los marcadores: el margen (px) y que no se acerque de más con uno solo\nconst FIT_PADDING_PIXELS = 48;\nconst FIT_MAX_ZOOM = 13;\nconst DEFAULT_HEIGHT_PIXELS = 420;\n\n/**\n * Acomoda el mapa para que se vean todos los marcadores cada vez que cambian.\n *\n * @param {{bounds: [[number, number], [number, number]]|null}} props\n */\nfunction FitToMarkers({bounds}) {\n const map = useMap();\n const boundsKey = JSON.stringify(bounds);\n useEffect(() => {\n if (bounds) {\n map.fitBounds(bounds, {padding: [FIT_PADDING_PIXELS, FIT_PADDING_PIXELS], maxZoom: FIT_MAX_ZOOM});\n }\n // OJO: depende del texto del recuadro y no del arreglo: el arreglo es nuevo en cada pintada y el mapa se movería solo\n }, [map, boundsKey]);\n return null;\n}\n\n/**\n * Vuela al marcador elegido y abre su globo.\n *\n * @param {{selectedMarker: object|undefined, markerRefs: object}} props\n */\nfunction FlyToSelected({selectedMarker, markerRefs}) {\n const map = useMap();\n useEffect(() => {\n if (!selectedMarker) {\n return;\n }\n map.flyTo(selectedMarker.position, Math.max(map.getZoom(), SELECTED_ZOOM));\n markerRefs.current.get(selectedMarker.id)?.openPopup();\n }, [map, selectedMarker, markerRefs]);\n return null;\n}\n\n/**\n * Avisa cada clic en el mapa (fuera de un marcador) con su posición.\n *\n * @param {{onMapClick: (position: [number, number]) => void}} props\n */\nfunction MapClicks({onMapClick}) {\n useMapEvents({click: (event) => onMapClick([event.latlng.lat, event.latlng.lng])});\n return null;\n}\n\n/**\n * Lo que dice el globo de un marcador cuando la pantalla no da el suyo: su título y su descripción.\n *\n * @param {{marker: {title: string, description?: string}}} props\n */\nfunction DefaultPopupContent({marker}) {\n return (\n <>\n <Typography variant=\"subtitle2\" component=\"p\">{marker.title}</Typography>\n {marker.description && <MapPopupText variant=\"body2\" color=\"text.secondary\" component=\"p\" ownSx={{m: 0}}>{marker.description}</MapPopupText>}\n </>\n );\n}\n\n/**\n * @param {object} props\n * @param {Array<{id: string, position: [number, number], title: string, description?: string, tone?: string} & Record<string, any>>} props.markers\n * tone: un color del tema (primary, success, warning, error, info…); por omisión primary\n * @param {string|null} [props.selectedId]\n * @param {(markerId: string) => void} [props.onSelect] al picar un marcador\n * @param {string} props.ariaLabel ya traducido: qué enseña el mapa\n * @param {number} [props.height] px\n * @param {{url: string, attribution: string}} [props.tileLayer] por omisión OpenStreetMap\n * @param {(marker: any) => import('react').ReactNode} [props.renderPopup] lo que dice el globo de un marcador (recibe el\n * marcador tal cual, con sus campos propios); por omisión su título y su descripción\n * @param {(position: [number, number]) => void} [props.onMapClick] al picar el mapa fuera de un marcador\n * @param {import('react').ReactNode} [props.children] capas propias de react-leaflet (Circle, Polygon, GeoJSON…)\n * @param {object} [props.mapProps] lo demás de MapContainer de react-leaflet (scrollWheelZoom, minZoom…)\n */\nexport default function MapView(props) {\n // lo que el proyecto puso en su tema (components.CalcitaMapView.defaultProps) y encima lo que llega aquí\n const {\n markers, selectedId = null, onSelect, ariaLabel, height = DEFAULT_HEIGHT_PIXELS, tileLayer = OPEN_STREET_MAP_TILES, renderPopup, onMapClick,\n children, mapProps,\n ...unknownProps\n} = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const theme = useTheme();\n const palette = paletteOf(theme);\n const markerRefs = useRef(new Map());\n const bounds = useMemo(() => boundsOfMarkers(markers), [markers]);\n const selectedMarker = markers.find((marker) => marker.id === selectedId);\n\n // el ícono de cada marcador: se arma de nuevo solo si cambia su color o si se elige\n const iconOf = (marker) => {\n const isSelected = marker.id === selectedId;\n const size = isSelected ? MAP_PIN_SIZE.selected : MAP_PIN_SIZE.normal;\n const color = (palette[marker.tone] ?? palette.primary).main;\n return L.divIcon({html: mapPinHtmlOf(color, isSelected), className: '', iconSize: [size, size], iconAnchor: [size / 2, size], popupAnchor: [0, -size]});\n };\n\n return (\n <MapRoot role=\"region\" aria-label={ariaLabel} ownSx={(currentTheme) => ({\n height, borderRadius: 3, overflow: 'hidden', position: 'relative', zIndex: 0,\n '& .leaflet-container': {height: '100%', width: '100%', fontFamily: 'inherit', bgcolor: 'action.hover'},\n '& .leaflet-bar': {border: 0, borderRadius: '10px', overflow: 'hidden', boxShadow: currentTheme.shadows[2]},\n '& .leaflet-bar a': {bgcolor: 'background.paper', color: 'text.primary', borderBottomColor: 'divider'},\n '& .leaflet-popup-content-wrapper, & .leaflet-popup-tip': {bgcolor: 'background.paper', color: 'text.primary'},\n '& .leaflet-popup-content-wrapper': {borderRadius: '12px'},\n '& .leaflet-control-attribution': {bgcolor: 'background.paper', color: 'text.secondary', '& a': {color: 'inherit'}},\n // OpenStreetMap solo da mosaicos claros: en oscuro se invierten y se regresa el tono (el agua sigue azul)\n ...currentTheme.applyStyles('dark', {'& .leaflet-tile-pane': {filter: 'invert(1) hue-rotate(180deg) brightness(0.95) contrast(0.9)'}}),\n })}>\n <MapContainer center={markers[0]?.position ?? DEFAULT_CENTER} zoom={DEFAULT_ZOOM} scrollWheelZoom={false} {...mapProps}>\n <TileLayer url={tileLayer.url} attribution={tileLayer.attribution} />\n {onMapClick && <MapClicks onMapClick={onMapClick} />}\n {children}\n <FitToMarkers bounds={bounds} />\n <FlyToSelected selectedMarker={selectedMarker} markerRefs={markerRefs} />\n {markers.map((marker) => (\n <Marker key={marker.id} position={marker.position} icon={iconOf(marker)} title={marker.title} alt={marker.title}\n zIndexOffset={marker.id === selectedId ? 1000 : 0}\n ref={(leafletMarker) => (leafletMarker ? markerRefs.current.set(marker.id, leafletMarker) : markerRefs.current.delete(marker.id))}\n eventHandlers={{click: () => onSelect?.(marker.id)}}>\n <Popup>\n {renderPopup ? renderPopup(marker) : <DefaultPopupContent marker={marker} />}\n </Popup>\n </Marker>\n ))}\n </MapContainer>\n </MapRoot>\n );\n}\n"],"names":[],"mappings":"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"}
|