@stackcraftmx/calcita 0.25.0 → 0.27.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.
Files changed (60) hide show
  1. package/README.md +29 -0
  2. package/dist/api/createApiClient.js +5 -0
  3. package/dist/api/createApiClient.js.map +1 -1
  4. package/dist/appShell/AccountMenu.js +15 -23
  5. package/dist/appShell/AccountMenu.js.map +1 -1
  6. package/dist/appShell/AppLayout.js +15 -8
  7. package/dist/appShell/AppLayout.js.map +1 -1
  8. package/dist/appShell/HeaderTools.js +4 -2
  9. package/dist/appShell/HeaderTools.js.map +1 -1
  10. package/dist/appShell/MenuLinks.js +36 -0
  11. package/dist/appShell/MenuLinks.js.map +1 -0
  12. package/dist/appShell/MobileTabBar.js +3 -3
  13. package/dist/appShell/MobileTabBar.js.map +1 -1
  14. package/dist/appShell/SettingsMenu.js +10 -5
  15. package/dist/appShell/SettingsMenu.js.map +1 -1
  16. package/dist/appShell/SideMenu.js +5 -3
  17. package/dist/appShell/SideMenu.js.map +1 -1
  18. package/dist/appShell/SideMenuEntry.js +3 -3
  19. package/dist/appShell/SideMenuEntry.js.map +1 -1
  20. package/dist/appShell/SubmenuActionsMenu.js +3 -4
  21. package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
  22. package/dist/appShell/TopMenu.js +5 -6
  23. package/dist/appShell/TopMenu.js.map +1 -1
  24. package/dist/appShell/Wordmark.js +11 -0
  25. package/dist/appShell/Wordmark.js.map +1 -0
  26. package/dist/appShell/menuItemText.js +4 -0
  27. package/dist/appShell/menuItemText.js.map +1 -1
  28. package/dist/index.js +8 -4
  29. package/dist/navigation/appMenu.js +13 -1
  30. package/dist/navigation/appMenu.js.map +1 -1
  31. package/dist/observability/logger.js +18 -0
  32. package/dist/observability/logger.js.map +1 -1
  33. package/dist/theme/components/MuiAppBar.js +6 -5
  34. package/dist/theme/components/MuiAppBar.js.map +1 -1
  35. package/dist/theme/components/glassComponents.js +32 -31
  36. package/dist/theme/components/glassComponents.js.map +1 -1
  37. package/dist/theme/components/minimalComponents.js +28 -9
  38. package/dist/theme/components/minimalComponents.js.map +1 -1
  39. package/dist/theme/createToolkitTheme.js +6 -3
  40. package/dist/theme/createToolkitTheme.js.map +1 -1
  41. package/dist/theme/glassSurface.js +36 -38
  42. package/dist/theme/glassSurface.js.map +1 -1
  43. package/dist/theme/lightDarkStyles.js +48 -0
  44. package/dist/theme/lightDarkStyles.js.map +1 -0
  45. package/dist/types/api/createApiClient.d.ts +17 -0
  46. package/dist/types/appShell/AppLayout.d.ts +5 -1
  47. package/dist/types/appShell/MenuLinks.d.ts +17 -0
  48. package/dist/types/appShell/SettingsMenu.d.ts +3 -1
  49. package/dist/types/appShell/SideMenu.d.ts +3 -1
  50. package/dist/types/appShell/Wordmark.d.ts +6 -0
  51. package/dist/types/appShell/menuItemText.d.ts +16 -0
  52. package/dist/types/index.d.ts +4 -4
  53. package/dist/types/navigation/appMenu.d.ts +8 -0
  54. package/dist/types/observability/logger.d.ts +13 -0
  55. package/dist/types/theme/components/MuiAppBar.d.ts +3 -1
  56. package/dist/types/theme/components/glassComponents.d.ts +5 -0
  57. package/dist/types/theme/components/minimalComponents.d.ts +49 -3
  58. package/dist/types/theme/createToolkitTheme.d.ts +21 -1
  59. package/dist/types/theme/lightDarkStyles.d.ts +8 -0
  60. package/package.json +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"AppLayout.js","sources":["../../src/appShell/AppLayout.jsx"],"sourcesContent":["// El marco de las pantallas con sesión, al estilo de las plantillas de MUI (y del marco de walk-ot). Dos estilos de\n// barra con las mismas props, para que el proyecto (o el `init` del CLI) solo elija:\n//\n// menuPosition=\"side\" (por omisión) menuPosition=\"top\" (apps de pocos módulos)\n// ┌──────────────────────────────────────────┐ ┌──────────────────────────────────────────┐\n// │ ☰ Logo Nombre · Título al deslizar · ⚙ (A)│ │ Logo · Inicio Pedidos Clientes ▾ · ⚙ (A) │\n// ├────────┬─────────────────────────────────┤ ├──────────────────────────────────────────┤\n// │ Menú │ contenido │ │ contenido │\n// │ por │ │ │ │\n// │ secc. │ │ │ │\n// └────────┴─────────────────────────────────┘ └──────────────────────────────────────────┘\n//\n// En el teléfono los dos son iguales: hamburguesa, título y ajustes en la barra; el menú sale en un cajón con el logo.\n//\n// Lo que trae, con su porqué:\n// - La barra va a todo lo ancho, con el logo y el nombre de la app a la izquierda, y el menú lateral debajo de ella\n// (como el DashboardLayout de MUI). La ☰ de la barra encoge el menú a solo íconos y lo recuerda (useDrawerCollapsed);\n// antes era un botón montado en la orilla del menú y se encimaba con él.\n// - El título de la página sube a la barra cuando su título grande sale de la vista (pageTitle.jsx).\n// - Escritorio y teléfono se eligen con CSS, no con useMediaQuery: el servidor (Next.js) ya pinta lo correcto.\n// - Dentro de la barra, para que se queden pegados al deslizar: los avisos del proyecto (ribbons), el de versión nueva\n// (NewBuildNotice; con vidrio flota aparte en su cápsula, ver NewBuildNotice.jsx) y la rayita de «cargando la pantalla»\n// (isNavigating). La barra publica su alto real, y el menú lateral empieza justo debajo de ella aunque crezca con un aviso.\n// - Con vidrio, en el teléfono la barra flota ABAJO en una cápsula, como la de Safari en el iPhone; y en escritorio, con\n// el menú encogido, la barra desaparece y se funde con el menú (☰ y logo arriba, ajustes y cuenta abajo).\n// - El primer Tab lleva a «Saltar al contenido» (WCAG 2.4.1) y el contenido es <main>. Respeta el área segura del\n// iPhone (la muesca) cuando la app corre instalada.\n// - Huecos para extender sin tocar el framework: headerActions, sidebarFooter, accountMenuItems, accountSettings, ribbons.\n// - Los ajustes de la app (letra, idioma, claro u oscuro y los del proyecto) van DENTRO del menú de la cuenta, no sueltos\n// en la barra (HeaderTools).\n// - El menú queda al alcance de las páginas (appMenuContext): PageHeading arma sus migas sola.\n// - Con el vidrio del tema (createToolkitTheme({glass: true})): el lienzo lleva manchas de color, y en escritorio la\n// barra y el menú lateral flotan a 8 px de la orilla y entre ellos, con esquinas redondas, para que el contenido pase\n// por detrás y se vea desenfocado; encogido, el menú es una cápsula. En el teléfono la barra sigue de orilla a orilla\n// (como en iOS). Sin vidrio, todo igual que siempre: el marco lo sabe por el tema (isGlassTheme), sin otra prop.\n// - Con vidrio y con el minimalista la barra no lleva franja, como las ventanas de macOS Tahoe (TOP_BAR.capsules, lo de\n// omisión): la ☰ con el nombre, el menú de arriba y la cuenta van cada uno en su cápsula, y el título queda suelto.\n// Con vidrio las cápsulas son de vidrio y el contenido pasa por debajo, desenfocado en la orilla (scrollEdgeStylesOf);\n// con el minimalista son bloques del tono de las tarjetas sobre la barra del color del fondo, también en el teléfono.\n// Con cápsulas el menú encogido ya no se funde con la barra: la cuenta y los ajustes se quedan arriba. Con\n// bar: TOP_BAR.band, la franja de lado a lado. Mismo alto y mismo lugar las dos: el menú lateral y las páginas no se\n// mueven.\n//\n// Lo renderiza la app: arrow (regla de la casa).\nimport {useMemo, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation, useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport AppBar from '@mui/material/AppBar';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Drawer from '@mui/material/Drawer';\nimport Fade from '@mui/material/Fade';\nimport IconButton from '@mui/material/IconButton';\nimport LinearProgress from '@mui/material/LinearProgress';\nimport Link from '@mui/material/Link';\nimport Toolbar from '@mui/material/Toolbar';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport MenuIcon from '@mui/icons-material/Menu';\nimport MenuOpenIcon from '@mui/icons-material/MenuOpen';\n\nimport {AppMenuContext} from '@/appShell/appMenuContext.js';\nimport BarCapsule from '@/appShell/BarCapsule.jsx';\nimport {BarActionsSlotContext} from '@/appShell/barActionsSlot.js';\nimport HeaderTools from '@/appShell/HeaderTools.jsx';\nimport MobileTabBar from '@/appShell/MobileTabBar.jsx';\nimport {PageTitleProvider, usePageTitle} from '@/appShell/pageTitle.jsx';\nimport {\n APP_BAR_HEIGHT_PIXELS, DESKTOP_MEDIA_QUERY, DRAWER_WIDTH_COLLAPSED, DRAWER_WIDTH_EXPANDED, GLASS_PANEL_GAP_PIXELS, MAIN_CONTENT_ID,\n FLOATING_BOTTOM_SPACE_CSS_VARIABLE, MOBILE_TAB_BAR_HEIGHT_PIXELS, PHONE_MEDIA_QUERY, drawerTransitionOf,\n} from '@/appShell/shellSizes.js';\nimport SideMenu from '@/appShell/SideMenu.jsx';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport TopMenu from '@/appShell/TopMenu.jsx';\nimport {useDrawerCollapsed} from '@/appShell/useDrawerCollapsed.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport NewBuildNotice, {NEW_BUILD_NOTICE_VARIANT} from '@/feedback/NewBuildNotice.jsx';\nimport {activeMenuItemOf} from '@/navigation/appMenu.js';\nimport {useNavigationTrail} from '@/navigationTrail.js';\nimport {THEME_LOOK, TOP_BAR} from '@/theme/createToolkitTheme.js';\nimport {GLASS_PANEL_RADIUS_PIXELS, ambientBackgroundOf, isGlassTheme, scrollEdgeStylesOf} from '@/theme/glassSurface.js';\nimport GlassLensFilter from '@/ui/layout/GlassLensFilter.jsx';\nimport {APP_BAR_HEIGHT, useAppBarHeightPublisher} from '@/ui/layout/appBarHeight.js';\n\n/** Los dos estilos de barra. */\nexport const MENU_POSITION = Object.freeze({side: 'side', top: 'top'});\n\nconst NAVIGATING_BAR_PIXELS = 2;\n\n/**\n * Un grupo de la barra: con la barra en cápsulas, su cápsula (BarCapsule: de vidrio, o con el minimalista un bloque del\n * tono de las tarjetas); con la franja, sus piezas tal cual (sin caja de más, para que la franja se quede idéntica).\n */\nconst BarGroup = ({isCapsule, sx, children}) => {\n if (!isCapsule) {\n return children;\n }\n // presentation: lo de adentro (la ☰, el nombre, la cuenta) ya se anuncia solo; el grupo es solo la forma\n return <BarCapsule role=\"presentation\" sx={sx}>{children}</BarCapsule>;\n};\n\n/** «Saltar al contenido»: escondido arriba hasta que llega el foco del teclado. */\nconst SkipToContentLink = () => {\n const {t} = useToolkitTranslation('layout');\n return (\n <Link href={`#${MAIN_CONTENT_ID}`} sx={{\n position: 'absolute', left: 8, top: -48, zIndex: (theme) => theme.zIndex.tooltip + 1, bgcolor: 'background.paper',\n px: 1.5, py: 0.75, borderRadius: 1, boxShadow: 3, '&:focus': {top: 8},\n }}>\n {t('skipToContent')}\n </Link>\n );\n};\n\n/** El logo y el nombre de la app en la barra (con su renglón chico, si lo hay); lleva al inicio. */\nconst BarBrand = ({logo, brandName, brandCaption, homePath, sx}) => {\n const {Link: RouterLink} = useToolkitNavigation();\n return (\n <ButtonBase component={RouterLink} to={homePath}\n sx={[{alignItems: 'center', gap: 1.25, borderRadius: 1, flexShrink: 0, minWidth: 0, pr: 1, textAlign: 'left'}, sx]}>\n {logo && <Box sx={{width: 28, height: 28, flex: '0 0 auto', display: 'flex', alignItems: 'center', justifyContent: 'center'}}>{logo}</Box>}\n <Box sx={{minWidth: 0}}>\n <Typography variant=\"subtitle2\" component=\"span\" noWrap sx={{display: 'block', fontWeight: 600, lineHeight: 1.3}}>{brandName}</Typography>\n {/* el renglón chico hereda el color de la barra (sobre el naranja, el gris del texto secundario no se leía) */}\n {brandCaption && <Typography variant=\"caption\" component=\"span\" noWrap sx={{display: 'block', lineHeight: 1.3, opacity: 0.8}}>{brandCaption}</Typography>}\n </Box>\n </ButtonBase>\n );\n};\n\n/**\n * El título en la barra: en escritorio, después del nombre de la app y solo cuando el título grande ya no se ve; en el\n * teléfono siempre (o el nombre de la app).\n */\nconst BarTitle = ({brandName}) => {\n const {title, isTitleVisible} = usePageTitle();\n return (\n <Box sx={{flexGrow: 1, minWidth: 0}}>\n <Typography variant=\"subtitle1\" component=\"p\" noWrap sx={{display: 'block', [DESKTOP_MEDIA_QUERY]: {display: 'none'}, fontWeight: 600}}>{title ?? brandName}</Typography>\n <Fade in={Boolean(title) && !isTitleVisible}>\n <Typography variant=\"subtitle1\" component=\"p\" noWrap\n sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'block'}, fontWeight: 600, ml: 1, pl: 2, borderLeft: '1px solid color-mix(in srgb, currentColor 30%, transparent)'}}>\n {title}\n </Typography>\n </Fade>\n </Box>\n );\n};\n\n/** El logo solo, arriba del menú encogido que hace de barra; lleva al inicio. */\nconst RailBrand = ({logo, brandName, homePath}) => {\n const {Link: RouterLink} = useToolkitNavigation();\n return (\n <Tooltip title={brandName} placement=\"right\">\n <ButtonBase component={RouterLink} to={homePath} aria-label={brandName}\n sx={{width: 44, height: 44, borderRadius: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center'}}>\n <Box sx={{width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center'}}>{logo}</Box>\n </ButtonBase>\n </Tooltip>\n );\n};\n\n/** La ☰ de escritorio: encoge el menú lateral a solo íconos o lo vuelve a abrir. */\nconst CollapseMenuButton = ({collapsed, onToggle}) => {\n const {t} = useToolkitTranslation('layout');\n const label = collapsed ? t('drawer.expand') : t('drawer.collapse');\n return (\n <Tooltip title={label}>\n <IconButton aria-label={label} aria-expanded={!collapsed} onClick={onToggle} sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'inline-flex'}, color: 'inherit'}}>\n {collapsed ? <MenuIcon /> : <MenuOpenIcon />}\n </IconButton>\n </Tooltip>\n );\n};\n\nconst AppLayoutFrame = ({\n logo, brandName, brandCaption, homePath, menuItems, menuPosition, user, profilePath, accountMenuItems, onSignOut, isSigningOut, headerActions, accountSettings, menuBadges,\n sidebarFooter, showFontSizeToggle, showLanguageToggle, showColorSchemeToggle, ribbons, isNavigating, defaultCollapsed, minimizeTabBarOnScroll, contentMaxWidth,\n onMenuCollapse, onMobileMenuChange, onMobileTabsChange, children,\n}) => {\n const {t} = useToolkitTranslation('layout');\n const {useLocation} = useToolkitNavigation();\n const {pathname} = useLocation();\n const {collapsed, toggle} = useDrawerCollapsed(defaultCollapsed);\n const [isMobileMenuOpen, setIsMobileMenuOpenState] = useState(false);\n // cada cambio del menú también sale afuera (para medir, o para que el proyecto guarde la preferencia en su servidor)\n const toggleCollapsed = () => {\n toggle();\n onMenuCollapse?.(!collapsed);\n };\n const setIsMobileMenuOpen = (isOpen) => {\n setIsMobileMenuOpenState(isOpen);\n onMobileMenuChange?.(isOpen);\n };\n const appBarRef = useAppBarHeightPublisher();\n useNavigationTrail();\n\n const theme = useTheme();\n const isGlass = isGlassTheme(theme);\n // la barra de pestañas del teléfono (estilo Apple): con vidrio, y con el minimalista en cápsulas (usesMobileTabBar);\n // sin vidrio, o con el minimalista en franja, se queda la barra de MUI arriba con su cajón\n const usesTabBar = usesMobileTabBar(theme);\n\n const isSideMenu = menuPosition !== MENU_POSITION.top;\n // con vidrio y el menú encogido, en escritorio la barra de arriba desaparece y se funde con el menú: la ☰ y el logo van\n // arriba del menú y los ajustes y la cuenta abajo (lo pidió el autor). Si el proyecto trae avisos en la barra (ribbons),\n // la barra se queda: los avisos no tendrían dónde ir\n // la barra sin franja, en cápsulas (lo de omisión con vidrio y con el minimalista; TOP_BAR.band pide la franja). El\n // estilo de Stackcraft sin vidrio se queda con su barra de siempre\n const isMinimalLook = theme.toolkit?.look === THEME_LOOK.minimal;\n const usesBarCapsules = (isGlass || isMinimalLook) && theme.toolkit?.bar === TOP_BAR.capsules;\n // con vidrio, o con cápsulas, la barra y el menú lateral van a GLASS_PANEL_GAP_PIXELS de la orilla: la columna del menú\n // mide eso de más. OJO: con el minimalista en cápsulas era 0 y las cápsulas quedaban pegadas arriba (a 4 px) y el menú\n // pegado a la izquierda (lo reportó el autor; medido en Chrome)\n const panelGap = isGlass || usesBarCapsules ? GLASS_PANEL_GAP_PIXELS : 0;\n // con cápsulas no se funde: no hay franja que esconder, y la cuenta y los ajustes se quedan arriba en su cápsula en\n // lugar de bajar al pie del menú encogido (lo pidió el autor)\n const isBarMerged = isGlass && isSideMenu && collapsed && !ribbons && !usesBarCapsules;\n const drawerWidth = collapsed ? DRAWER_WIDTH_COLLAPSED : DRAWER_WIDTH_EXPANDED;\n const activeItem = activeMenuItemOf(menuItems, pathname);\n const sideMenuProps = {logo, brandName, brandCaption, homePath, menuItems, activeItem, menuBadges, footer: sidebarFooter};\n // el lugar de la barra donde caen las acciones de cada pantalla (BarActions); con la barra fundida con el menú no hay\n // barra en escritorio y se quedan en la pantalla\n const [barActionsSlot, setBarActionsSlot] = useState(null);\n // en el teléfono con cápsulas no hay barra de arriba (la de pestañas va abajo): las acciones flotan arriba a la derecha\n const usesPhoneFloatingActions = usesTabBar && usesBarCapsules;\n const [phoneBarActionsSlot, setPhoneBarActionsSlot] = useState(null);\n const barActions = useMemo(() => ({\n slot: isBarMerged ? null : barActionsSlot,\n phoneSlot: usesPhoneFloatingActions ? phoneBarActionsSlot : null,\n usesCapsules: usesBarCapsules,\n }), [isBarMerged, barActionsSlot, usesPhoneFloatingActions, phoneBarActionsSlot, usesBarCapsules]);\n\n return (\n <BarActionsSlotContext.Provider value={barActions}>\n <Box sx={[{display: 'flex', flexDirection: 'column', minHeight: '100vh', bgcolor: 'background.default'}, isGlass && ambientBackgroundOf]}>\n <SkipToContentLink />\n <GlassLensFilter />\n\n {/* en cápsulas, lo que pasa por debajo de la barra se desenfoca en la orilla: debajo del menú lateral, que sigue nítido */}\n {isGlass && usesBarCapsules && (\n <Box aria-hidden=\"true\"\n sx={(theme) => ({\n ...scrollEdgeStylesOf(theme), display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'block'},\n position: 'fixed', top: 0, left: 0, right: 0, height: `calc(${APP_BAR_HEIGHT} + ${panelGap}px)`,\n zIndex: theme.zIndex.drawer - 1, pointerEvents: 'none',\n })} />\n )}\n {/* en cápsulas con vidrio la barra va transparente (color=\"transparent\": el tema no le pone vidrio) y cada grupo\n lleva el suyo; con el minimalista se queda del color del fondo, que tapa lo que pasa por debajo */}\n <AppBar ref={appBarRef} position=\"sticky\" color={isGlass && usesBarCapsules ? 'transparent' : undefined}\n sx={(theme) => ({\n pt: 'env(safe-area-inset-top)',\n // encima del menú lateral, como en el DashboardLayout de MUI\n zIndex: theme.zIndex.drawer + 1,\n // con vidrio flota: en escritorio arriba, separada de las orillas; en el teléfono ABAJO, en una cápsula,\n // como la barra de Safari en el iPhone (en el teléfono se veía cuadrada y pegada arriba; lo reportaron).\n // Abajo se suma la zona del gesto de inicio del iPhone (safe-area) a la separación\n ...(isGlass && {\n // en cápsulas no se recorta: la sombra de cada cápsula se saldría cortada\n overflow: usesBarCapsules ? 'visible' : 'hidden',\n [DESKTOP_MEDIA_QUERY]: {\n top: panelGap, mt: `${panelGap}px`, mx: `${panelGap}px`, width: 'auto',\n borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`,\n // sin la raya de abajo de la barra fija: no hay franja que separar\n ...(usesBarCapsules && {borderBottom: 0, boxShadow: 'none'}),\n ...(isBarMerged && {display: 'none'}),\n },\n }),\n // el minimalista en cápsulas no flota (su barra es del color del fondo y tapa lo que pasa por debajo):\n // la separación de las orillas va por dentro, para que las cápsulas queden donde las del vidrio\n ...(!isGlass && usesBarCapsules && {\n [DESKTOP_MEDIA_QUERY]: {pt: `calc(env(safe-area-inset-top) + ${panelGap}px)`, px: `${panelGap}px`},\n }),\n // en el teléfono la barra de pestañas (MobileTabBar) la reemplaza: con ella van el menú y la cuenta\n ...(usesTabBar && {[PHONE_MEDIA_QUERY]: {display: 'none'}}),\n })}>\n {/* a la izquierda, la ☰ queda alineada con los íconos del menú lateral (los dos a 32 px de su orilla) */}\n <Toolbar disableGutters sx={{minHeight: {xs: APP_BAR_HEIGHT_PIXELS, sm: APP_BAR_HEIGHT_PIXELS}, gap: 1, pl: {xs: 1.5, md: isSideMenu ? 1.5 : 3}, pr: {xs: 1.5, md: 3}}}>\n {/* la ☰ del teléfono va en la primera cápsula (en escritorio la de encoger el menú y el nombre). OJO: dentro de\n una cápsula, sin el margen negativo de «orilla de inicio» de MUI (-12 px): el botón quedaba pegado a la\n curva. Así su círculo queda concéntrico con la cápsula */}\n <BarGroup isCapsule={usesBarCapsules}>\n <IconButton edge=\"start\" aria-label={t('drawer.open')} aria-expanded={isMobileMenuOpen} onClick={() => setIsMobileMenuOpen(true)}\n sx={{display: 'inline-flex', [DESKTOP_MEDIA_QUERY]: {display: 'none'}, color: 'inherit', ...((isGlass || usesBarCapsules) && {ml: 0})}}>\n <MenuIcon />\n </IconButton>\n {isSideMenu && <CollapseMenuButton collapsed={collapsed} onToggle={toggleCollapsed} />}\n <BarBrand logo={logo} brandName={brandName} brandCaption={brandCaption} homePath={homePath}\n sx={isSideMenu ? {display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'flex'}} : {display: 'flex'}} />\n </BarGroup>\n {isSideMenu ? (\n <BarTitle brandName={brandName} />\n ) : (\n <>\n {/* el menú de arriba no sale en el teléfono: su cápsula tampoco (vacía se vería) */}\n <BarGroup isCapsule={usesBarCapsules} sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'flex'}}}>\n <TopMenu menuItems={menuItems} activeItem={activeItem} />\n </BarGroup>\n <Box sx={{flexGrow: 1}} />\n </>\n )}\n {/* las acciones de la pantalla abierta (BarActions, y las de PageHeading con cápsulas): cada grupo cae aquí,\n antes de la cuenta. Solo en escritorio: en el teléfono flotan arriba (phoneBarActionsSlot, en el contenido) */}\n <Box ref={setBarActionsSlot} sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'flex'}, alignItems: 'center', gap: 1, minWidth: 0}} />\n <BarGroup isCapsule={usesBarCapsules}>\n <HeaderTools headerActions={headerActions} accountSettings={accountSettings} showFontSizeToggle={showFontSizeToggle} showLanguageToggle={showLanguageToggle}\n showColorSchemeToggle={showColorSchemeToggle} user={user} profilePath={profilePath} accountMenuItems={accountMenuItems}\n onSignOut={onSignOut} isSigningOut={isSigningOut} />\n </BarGroup>\n </Toolbar>\n {ribbons}\n {/* con la barra de pestañas el aviso de versión nueva flota aparte, en su cápsula (en el teléfono esta barra no\n se ve); sin ella, franja dentro de la barra */}\n {!usesTabBar && <NewBuildNotice />}\n {isNavigating && (\n <LinearProgress aria-label={t('navigating')} color=\"inherit\"\n sx={{position: 'absolute', bottom: 0, left: 0, right: 0, height: NAVIGATING_BAR_PIXELS}} />\n )}\n </AppBar>\n {usesTabBar && <NewBuildNotice variant={NEW_BUILD_NOTICE_VARIANT.floating} />}\n {usesTabBar && (\n <MobileTabBar menuItems={menuItems} activeItem={activeItem} menuBadges={menuBadges} moreFooter={sidebarFooter} minimizeOnScroll={minimizeTabBarOnScroll}\n onPinnedTabsChange={onMobileTabsChange}\n account={(\n <HeaderTools headerActions={headerActions} accountSettings={accountSettings} showFontSizeToggle={showFontSizeToggle}\n showLanguageToggle={showLanguageToggle} showColorSchemeToggle={showColorSchemeToggle} user={user}\n profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut} isSigningOut={isSigningOut} />\n )} />\n )}\n\n <Box sx={{display: 'flex', flexGrow: 1, minWidth: 0}}>\n {isSideMenu && (\n <Drawer variant=\"permanent\"\n sx={(theme) => ({\n display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'block'}, width: drawerWidth + panelGap, flexShrink: 0, whiteSpace: 'nowrap',\n transition: drawerTransitionOf(theme),\n '& .MuiDrawer-paper': {\n width: drawerWidth, left: panelGap, boxSizing: 'border-box', overflowX: 'hidden', transition: drawerTransitionOf(theme, ['width']),\n // justo debajo de la barra (su alto real, con avisos o sin ellos) y hasta abajo; si la barra se\n // fundió con el menú, de arriba a abajo\n ...(isBarMerged\n ? {top: panelGap, height: `calc(100% - ${panelGap * 2}px)`}\n : {top: `calc(${APP_BAR_HEIGHT} + ${panelGap}px)`, height: `calc(100% - ${APP_BAR_HEIGHT} - ${panelGap * 2}px)`}),\n // el panel flotante: separado de las orillas y sin la línea de la derecha (lo separa su brillo).\n // Encogido es una cápsula (radio de la mitad de su ancho), como las barras de pestañas de vidrio\n ...(isGlass && {\n border: 0,\n borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`,\n }),\n },\n })}>\n <SideMenu {...sideMenuProps} showBrand={false} collapsed={collapsed}\n railTop={isBarMerged ? (\n <>\n <CollapseMenuButton collapsed={collapsed} onToggle={toggleCollapsed} />\n {logo && <RailBrand logo={logo} brandName={brandName} homePath={homePath} />}\n </>\n ) : undefined}\n railBottom={isBarMerged ? (\n <HeaderTools vertical headerActions={headerActions} accountSettings={accountSettings} showFontSizeToggle={showFontSizeToggle}\n showLanguageToggle={showLanguageToggle} showColorSchemeToggle={showColorSchemeToggle} user={user}\n profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut}\n isSigningOut={isSigningOut} />\n ) : undefined} />\n </Drawer>\n )}\n\n <Box component=\"main\" id={MAIN_CONTENT_ID} tabIndex={-1}\n sx={(theme) => ({\n flexGrow: 1, minWidth: 0, outline: 'none', px: {xs: 2, md: 3, lg: 4}, pt: 3, pb: 6,\n // con la barra de pestañas abajo (teléfono): arriba, lugar para la hora y la cámara del iPhone; abajo,\n // lugar para que la barra (que flota a GLASS_PANEL_GAP_PIXELS) no tape el final de la página\n ...(usesTabBar && {\n [PHONE_MEDIA_QUERY]: {\n pt: `calc(${theme.spacing(3)} + env(safe-area-inset-top))`,\n pb: `calc(${MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait + GLASS_PANEL_GAP_PIXELS * 2}px + env(safe-area-inset-bottom) + ${theme.spacing(3)} + var(${FLOATING_BOTTOM_SPACE_CSS_VARIABLE}, 0px))`,\n },\n }),\n })}>\n {/* teléfono con cápsulas: el renglón de las acciones de la pantalla, arriba a la derecha. Va pegado (sticky)\n y no fijo: así ocupa su lugar arriba de la página (no tapa las migas ni el título) y al bajar se queda\n flotando. Vacío no se ve. Solo las cápsulas reciben clics: el hueco de la izquierda deja pasar el dedo\n a lo de abajo */}\n {usesPhoneFloatingActions && (\n <Box ref={setPhoneBarActionsSlot} data-phone-bar-actions=\"\"\n sx={(theme) => ({\n display: 'none',\n [PHONE_MEDIA_QUERY]: {\n display: 'flex', '&:empty': {display: 'none'},\n position: 'sticky', top: `calc(env(safe-area-inset-top) + ${GLASS_PANEL_GAP_PIXELS}px)`, zIndex: theme.zIndex.appBar,\n justifyContent: 'flex-end', alignItems: 'center', gap: 1, mb: 2,\n pointerEvents: 'none', '& > *': {pointerEvents: 'auto'},\n },\n })} />\n )}\n <Box sx={{maxWidth: contentMaxWidth, mx: 'auto'}}>{children}</Box>\n </Box>\n </Box>\n\n {/* el cajón del teléfono (los dos estilos): con el logo arriba, se cierra al elegir una pantalla */}\n <Drawer variant=\"temporary\" open={isMobileMenuOpen} onClose={() => setIsMobileMenuOpen(false)} ModalProps={{keepMounted: true}}\n sx={{[DESKTOP_MEDIA_QUERY]: {display: 'none'}}}\n slotProps={{paper: {sx: {width: DRAWER_WIDTH_EXPANDED, boxSizing: 'border-box', pt: 'env(safe-area-inset-top)', pb: 'env(safe-area-inset-bottom)'}}}}>\n <SideMenu {...sideMenuProps} collapsed={false} onNavigate={() => setIsMobileMenuOpen(false)} />\n </Drawer>\n </Box>\n </BarActionsSlotContext.Provider>\n );\n};\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} [props.logo] el logo chico (28 px), en la barra junto al nombre y en el cajón del teléfono\n * @param {string} [props.brandName] el nombre de la app; por omisión el appName de ToolkitProvider\n * @param {string} [props.brandCaption] el renglón chico debajo del nombre («Panel de administración»)\n * @param {string} [props.homePath] por omisión '/'\n * @param {object[]} [props.menuItems] lo que regresa composeMenu\n * @param {'side'|'top'} [props.menuPosition] por omisión 'side' (MENU_POSITION)\n * @param {{name: string, email?: string, subtitle?: string, avatarUrl?: string}|null} [props.user] quién está dentro\n * @param {string} [props.profilePath] la ruta de Mi perfil\n * @param {object[]} [props.accountMenuItems] renglones propios en el menú de la cuenta ({to | onClick, label | labelKey, Icon})\n * @param {() => void} [props.onSignOut] sin él no sale «Salir» (una app sin sesión)\n * @param {boolean} [props.isSigningOut]\n * @param {import('react').ReactNode} [props.headerActions] botones propios en la barra, antes de la cuenta\n * @param {Record<string, number>} [props.menuBadges] los conteos del servidor por badgeKey de cada entrada del menú\n * (useMenuBadges): lo pendiente de cada pantalla, como numerito\n * @param {import('react').ReactNode} [props.accountSettings] ajustes propios del proyecto dentro del menú de la cuenta, debajo de\n * letra, idioma y claro u oscuro (con SettingSection, para que se vean igual)\n * @param {import('react').ReactNode} [props.sidebarFooter] lo de abajo del menú lateral (una tarjeta de ayuda…)\n * @param {boolean} [props.showFontSizeToggle] por omisión sí\n * @param {boolean} [props.showLanguageToggle] por omisión sí\n * @param {boolean} [props.showColorSchemeToggle] por omisión sí\n * @param {import('react').ReactNode} [props.ribbons] avisos propios dentro de la barra\n * @param {boolean} [props.isNavigating] la pantalla siguiente se está descargando (react-router: useIsNavigating)\n * @param {boolean} [props.defaultCollapsed] el menú lateral empieza encogido (la persona lo cambia y se recuerda)\n * @param {boolean} [props.minimizeTabBarOnScroll] en el teléfono con vidrio, la barra de pestañas se encoge al leer hacia\n * abajo y regresa al subir (como iOS 26); por omisión sí\n * @param {number|string} [props.contentMaxWidth] por omisión 1400\n * @param {(collapsed: boolean) => void} [props.onMenuCollapse] la persona encogió (true) o abrió (false) el menú lateral\n * @param {(isOpen: boolean) => void} [props.onMobileMenuChange] se abrió o se cerró el menú del teléfono (el cajón)\n * @param {(keys: string[]|null) => void} [props.onMobileTabsChange] la persona eligió sus pestañas del teléfono (las llaves\n * de menuItemKeyOf; null: regresó a las de omisión). Se recuerdan en el navegador; con esto el proyecto las puede guardar\n * en su servidor\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete (para todo el marco)\n * @param {import('react').ReactNode} props.children el contenido\n */\nexport const AppLayout = ({\n logo, brandName, brandCaption, homePath = '/', menuItems = [], menuPosition = MENU_POSITION.side, user = null, profilePath, accountMenuItems = [], onSignOut,\n isSigningOut = false, headerActions, accountSettings, menuBadges, sidebarFooter, showFontSizeToggle = true, showLanguageToggle = true, showColorSchemeToggle = true, ribbons,\n isNavigating = false, defaultCollapsed = false, minimizeTabBarOnScroll = true, contentMaxWidth = 1400, onMenuCollapse, onMobileMenuChange, onMobileTabsChange,\n texts, children,\n}) => {\n const {appName} = useToolkitSettings();\n const appMenu = useMemo(() => ({menuItems}), [menuItems]);\n // los textos del marco llegan a todas sus piezas (la cuenta, el menú, las pestañas del teléfono), cada una lee los suyos\n return (\n <ComponentTexts namespace=\"layout\" texts={texts}>\n <PageTitleProvider>\n <AppMenuContext.Provider value={appMenu}>\n <AppLayoutFrame logo={logo} brandName={brandName ?? appName} brandCaption={brandCaption} homePath={homePath} menuItems={menuItems} menuPosition={menuPosition}\n user={user} profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut} isSigningOut={isSigningOut}\n headerActions={headerActions} accountSettings={accountSettings} menuBadges={menuBadges} sidebarFooter={sidebarFooter} showFontSizeToggle={showFontSizeToggle}\n showLanguageToggle={showLanguageToggle} showColorSchemeToggle={showColorSchemeToggle} ribbons={ribbons}\n isNavigating={isNavigating} defaultCollapsed={defaultCollapsed} minimizeTabBarOnScroll={minimizeTabBarOnScroll}\n contentMaxWidth={contentMaxWidth} onMenuCollapse={onMenuCollapse} onMobileMenuChange={onMobileMenuChange}\n onMobileTabsChange={onMobileTabsChange}>\n {children}\n </AppLayoutFrame>\n </AppMenuContext.Provider>\n </PageTitleProvider>\n </ComponentTexts>\n );\n};\n"],"names":["theme"],"mappings":"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"}
1
+ {"version":3,"file":"AppLayout.js","sources":["../../src/appShell/AppLayout.jsx"],"sourcesContent":["// El marco de las pantallas con sesión, al estilo de las plantillas de MUI (y del marco de walk-ot). Dos estilos de\n// barra con las mismas props, para que el proyecto (o el `init` del CLI) solo elija:\n//\n// menuPosition=\"side\" (por omisión) menuPosition=\"top\" (apps de pocos módulos)\n// ┌──────────────────────────────────────────┐ ┌──────────────────────────────────────────┐\n// │ ☰ Logo Nombre · Título al deslizar · ⚙ (A)│ │ Logo · Inicio Pedidos Clientes ▾ · ⚙ (A) │\n// ├────────┬─────────────────────────────────┤ ├──────────────────────────────────────────┤\n// │ Menú │ contenido │ │ contenido │\n// │ por │ │ │ │\n// │ secc. │ │ │ │\n// └────────┴─────────────────────────────────┘ └──────────────────────────────────────────┘\n//\n// En el teléfono los dos son iguales: hamburguesa, título y ajustes en la barra; el menú sale en un cajón con el logo.\n//\n// Lo que trae, con su porqué:\n// - La barra va a todo lo ancho, con el logo y el nombre de la app a la izquierda, y el menú lateral debajo de ella\n// (como el DashboardLayout de MUI). La ☰ de la barra encoge el menú a solo íconos y lo recuerda (useDrawerCollapsed);\n// antes era un botón montado en la orilla del menú y se encimaba con él.\n// - El título de la página sube a la barra cuando su título grande sale de la vista (pageTitle.jsx).\n// - Escritorio y teléfono se eligen con CSS, no con useMediaQuery: el servidor (Next.js) ya pinta lo correcto.\n// - Dentro de la barra, para que se queden pegados al deslizar: los avisos del proyecto (ribbons), el de versión nueva\n// (NewBuildNotice; con vidrio flota aparte en su cápsula, ver NewBuildNotice.jsx) y la rayita de «cargando la pantalla»\n// (isNavigating). La barra publica su alto real, y el menú lateral empieza justo debajo de ella aunque crezca con un aviso.\n// - Con vidrio, en el teléfono la barra flota ABAJO en una cápsula, como la de Safari en el iPhone; y en escritorio, con\n// el menú encogido, la barra desaparece y se funde con el menú (☰ y logo arriba, ajustes y cuenta abajo).\n// - El primer Tab lleva a «Saltar al contenido» (WCAG 2.4.1) y el contenido es <main>. Respeta el área segura del\n// iPhone (la muesca) cuando la app corre instalada.\n// - Huecos para extender sin tocar el framework: headerActions, sidebarFooter, accountMenuItems, accountSettings, ribbons.\n// - Los ajustes de la app (letra, idioma, claro u oscuro y los del proyecto) van DENTRO del menú de la cuenta, no sueltos\n// en la barra (HeaderTools).\n// - El menú queda al alcance de las páginas (appMenuContext): PageHeading arma sus migas sola.\n// - Con el vidrio del tema (createToolkitTheme({glass: true})): el lienzo lleva manchas de color, y en escritorio la\n// barra y el menú lateral flotan a 8 px de la orilla y entre ellos, con esquinas redondas, para que el contenido pase\n// por detrás y se vea desenfocado; encogido, el menú es una cápsula. En el teléfono la barra sigue de orilla a orilla\n// (como en iOS). Sin vidrio, todo igual que siempre: el marco lo sabe por el tema (isGlassTheme), sin otra prop.\n// - Con vidrio y con el minimalista la barra no lleva franja, como las ventanas de macOS Tahoe (TOP_BAR.capsules, lo de\n// omisión): la ☰ con el nombre, el menú de arriba y la cuenta van cada uno en su cápsula, y el título queda suelto.\n// Con vidrio las cápsulas son de vidrio y el contenido pasa por debajo, desenfocado en la orilla (scrollEdgeStylesOf);\n// con el minimalista son bloques del tono de las tarjetas sobre la barra del color del fondo, también en el teléfono.\n// Con cápsulas el menú encogido ya no se funde con la barra: la cuenta y los ajustes se quedan arriba. Con\n// bar: TOP_BAR.band, la franja de lado a lado. Mismo alto y mismo lugar las dos: el menú lateral y las páginas no se\n// mueven.\n//\n// Lo renderiza la app: arrow (regla de la casa).\nimport {useMemo, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation, useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport AppBar from '@mui/material/AppBar';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Drawer from '@mui/material/Drawer';\nimport Fade from '@mui/material/Fade';\nimport IconButton from '@mui/material/IconButton';\nimport LinearProgress from '@mui/material/LinearProgress';\nimport Link from '@mui/material/Link';\nimport Toolbar from '@mui/material/Toolbar';\nimport Tooltip from '@mui/material/Tooltip';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\nimport MenuIcon from '@mui/icons-material/Menu';\nimport MenuOpenIcon from '@mui/icons-material/MenuOpen';\n\nimport {AppMenuContext} from '@/appShell/appMenuContext.js';\nimport BarCapsule from '@/appShell/BarCapsule.jsx';\nimport {BarActionsSlotContext} from '@/appShell/barActionsSlot.js';\nimport HeaderTools from '@/appShell/HeaderTools.jsx';\nimport MobileTabBar from '@/appShell/MobileTabBar.jsx';\nimport Wordmark from '@/appShell/Wordmark.jsx';\nimport {PageTitleProvider, usePageTitle} from '@/appShell/pageTitle.jsx';\nimport {\n APP_BAR_HEIGHT_PIXELS, DESKTOP_MEDIA_QUERY, DRAWER_WIDTH_COLLAPSED, DRAWER_WIDTH_EXPANDED, GLASS_PANEL_GAP_PIXELS, MAIN_CONTENT_ID,\n FLOATING_BOTTOM_SPACE_CSS_VARIABLE, MOBILE_TAB_BAR_HEIGHT_PIXELS, PHONE_MEDIA_QUERY, drawerTransitionOf,\n} from '@/appShell/shellSizes.js';\nimport SideMenu from '@/appShell/SideMenu.jsx';\nimport {usesMobileTabBar} from '@/appShell/tabBarRule.js';\nimport TopMenu from '@/appShell/TopMenu.jsx';\nimport {useDrawerCollapsed} from '@/appShell/useDrawerCollapsed.js';\nimport ComponentTexts from '@/toolkit/ComponentTexts.jsx';\nimport NewBuildNotice, {NEW_BUILD_NOTICE_VARIANT} from '@/feedback/NewBuildNotice.jsx';\nimport {activeMenuItemOf, menuItemsWithIconsOf} from '@/navigation/appMenu.js';\nimport {useNavigationTrail} from '@/navigationTrail.js';\nimport {THEME_LOOK, TOP_BAR} from '@/theme/createToolkitTheme.js';\nimport {GLASS_PANEL_RADIUS_PIXELS, ambientBackgroundOf, isGlassTheme, scrollEdgeStylesOf} from '@/theme/glassSurface.js';\nimport GlassLensFilter from '@/ui/layout/GlassLensFilter.jsx';\nimport {APP_BAR_HEIGHT, useAppBarHeightPublisher} from '@/ui/layout/appBarHeight.js';\n\n/** Los dos estilos de barra. */\nexport const MENU_POSITION = Object.freeze({side: 'side', top: 'top'});\n\nconst NAVIGATING_BAR_PIXELS = 2;\n\n/**\n * Un grupo de la barra: con la barra en cápsulas, su cápsula (BarCapsule: de vidrio, o con el minimalista un bloque del\n * tono de las tarjetas); con la franja, sus piezas tal cual (sin caja de más, para que la franja se quede idéntica).\n */\nconst BarGroup = ({isCapsule, sx, children}) => {\n if (!isCapsule) {\n return children;\n }\n // presentation: lo de adentro (la ☰, el nombre, la cuenta) ya se anuncia solo; el grupo es solo la forma\n return <BarCapsule role=\"presentation\" sx={sx}>{children}</BarCapsule>;\n};\n\n/** «Saltar al contenido»: escondido arriba hasta que llega el foco del teclado. */\nconst SkipToContentLink = () => {\n const {t} = useToolkitTranslation('layout');\n return (\n <Link href={`#${MAIN_CONTENT_ID}`} sx={{\n position: 'absolute', left: 8, top: -48, zIndex: (theme) => theme.zIndex.tooltip + 1, bgcolor: 'background.paper',\n px: 1.5, py: 0.75, borderRadius: 1, boxShadow: 3, '&:focus': {top: 8},\n }}>\n {t('skipToContent')}\n </Link>\n );\n};\n\n/** El logo y el nombre de la app en la barra (con su renglón chico, si lo hay), o el logo ancho; lleva al inicio. */\nconst BarBrand = ({logo, wordmark, brandName, brandCaption, homePath, sx}) => {\n const {Link: RouterLink} = useToolkitNavigation();\n return (\n <ButtonBase component={RouterLink} to={homePath} aria-label={wordmark ? brandName : undefined}\n sx={[{alignItems: 'center', gap: 1.25, borderRadius: 1, flexShrink: 0, minWidth: 0, pr: 1, textAlign: 'left'}, sx]}>\n {wordmark ? <Wordmark>{wordmark}</Wordmark> : (\n <>\n {logo && <Box sx={{width: 28, height: 28, flex: '0 0 auto', display: 'flex', alignItems: 'center', justifyContent: 'center'}}>{logo}</Box>}\n <Box sx={{minWidth: 0}}>\n <Typography variant=\"subtitle2\" component=\"span\" noWrap sx={{display: 'block', fontWeight: 600, lineHeight: 1.3}}>{brandName}</Typography>\n {/* el renglón chico hereda el color de la barra (sobre el naranja, el gris del texto secundario no se leía) */}\n {brandCaption && <Typography variant=\"caption\" component=\"span\" noWrap sx={{display: 'block', lineHeight: 1.3, opacity: 0.8}}>{brandCaption}</Typography>}\n </Box>\n </>\n )}\n </ButtonBase>\n );\n};\n\n/**\n * El título en la barra: en escritorio, después del nombre de la app y solo cuando el título grande ya no se ve; en el\n * teléfono siempre (o el nombre de la app).\n */\nconst BarTitle = ({brandName}) => {\n const {title, isTitleVisible} = usePageTitle();\n return (\n <Box sx={{flexGrow: 1, minWidth: 0}}>\n <Typography variant=\"subtitle1\" component=\"p\" noWrap sx={{display: 'block', [DESKTOP_MEDIA_QUERY]: {display: 'none'}, fontWeight: 600}}>{title ?? brandName}</Typography>\n <Fade in={Boolean(title) && !isTitleVisible}>\n <Typography variant=\"subtitle1\" component=\"p\" noWrap\n sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'block'}, fontWeight: 600, ml: 1, pl: 2, borderLeft: '1px solid color-mix(in srgb, currentColor 30%, transparent)'}}>\n {title}\n </Typography>\n </Fade>\n </Box>\n );\n};\n\n/** El logo solo, arriba del menú encogido que hace de barra; lleva al inicio. */\nconst RailBrand = ({logo, brandName, homePath}) => {\n const {Link: RouterLink} = useToolkitNavigation();\n return (\n <Tooltip title={brandName} placement=\"right\">\n <ButtonBase component={RouterLink} to={homePath} aria-label={brandName}\n sx={{width: 44, height: 44, borderRadius: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center'}}>\n <Box sx={{width: 28, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center'}}>{logo}</Box>\n </ButtonBase>\n </Tooltip>\n );\n};\n\n/** La ☰ de escritorio: encoge el menú lateral a solo íconos o lo vuelve a abrir. */\nconst CollapseMenuButton = ({collapsed, onToggle}) => {\n const {t} = useToolkitTranslation('layout');\n const label = collapsed ? t('drawer.expand') : t('drawer.collapse');\n return (\n <Tooltip title={label}>\n <IconButton aria-label={label} aria-expanded={!collapsed} onClick={onToggle} sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'inline-flex'}, color: 'inherit'}}>\n {collapsed ? <MenuIcon /> : <MenuOpenIcon />}\n </IconButton>\n </Tooltip>\n );\n};\n\nconst AppLayoutFrame = ({\n logo, wordmark, brandName, brandCaption, homePath, menuItems, menuPosition, user, profilePath, accountMenuItems, onSignOut, isSigningOut, headerActions,\n accountSettings, menuBadges, sidebarFooter, showFontSizeToggle, showLanguageToggle, showColorSchemeToggle, ribbons, isNavigating, defaultCollapsed, minimizeTabBarOnScroll, contentMaxWidth,\n onMenuCollapse, onMobileMenuChange, onMobileTabsChange, children,\n}) => {\n const {t} = useToolkitTranslation('layout');\n const {useLocation} = useToolkitNavigation();\n const {pathname} = useLocation();\n const {collapsed, toggle} = useDrawerCollapsed(defaultCollapsed);\n const [isMobileMenuOpen, setIsMobileMenuOpenState] = useState(false);\n // cada cambio del menú también sale afuera (para medir, o para que el proyecto guarde la preferencia en su servidor)\n const toggleCollapsed = () => {\n toggle();\n onMenuCollapse?.(!collapsed);\n };\n const setIsMobileMenuOpen = (isOpen) => {\n setIsMobileMenuOpenState(isOpen);\n onMobileMenuChange?.(isOpen);\n };\n const appBarRef = useAppBarHeightPublisher();\n useNavigationTrail();\n\n const theme = useTheme();\n const isGlass = isGlassTheme(theme);\n // la barra de pestañas del teléfono (estilo Apple): con vidrio, y con el minimalista en cápsulas (usesMobileTabBar);\n // sin vidrio, o con el minimalista en franja, se queda la barra de MUI arriba con su cajón\n const usesTabBar = usesMobileTabBar(theme);\n\n const isSideMenu = menuPosition !== MENU_POSITION.top;\n // con vidrio y el menú encogido, en escritorio la barra de arriba desaparece y se funde con el menú: la ☰ y el logo van\n // arriba del menú y los ajustes y la cuenta abajo (lo pidió el autor). Si el proyecto trae avisos en la barra (ribbons),\n // la barra se queda: los avisos no tendrían dónde ir\n // la barra sin franja, en cápsulas (lo de omisión con vidrio y con el minimalista; TOP_BAR.band pide la franja). El\n // estilo de Stackcraft sin vidrio se queda con su barra de siempre\n const isMinimalLook = theme.toolkit?.look === THEME_LOOK.minimal;\n const usesBarCapsules = (isGlass || isMinimalLook) && theme.toolkit?.bar === TOP_BAR.capsules;\n // con vidrio, o con cápsulas, la barra y el menú lateral van a GLASS_PANEL_GAP_PIXELS de la orilla: la columna del menú\n // mide eso de más. OJO: con el minimalista en cápsulas era 0 y las cápsulas quedaban pegadas arriba (a 4 px) y el menú\n // pegado a la izquierda (lo reportó el autor; medido en Chrome)\n const panelGap = isGlass || usesBarCapsules ? GLASS_PANEL_GAP_PIXELS : 0;\n // con cápsulas no se funde: no hay franja que esconder, y la cuenta y los ajustes se quedan arriba en su cápsula en\n // lugar de bajar al pie del menú encogido (lo pidió el autor)\n const isBarMerged = isGlass && isSideMenu && collapsed && !ribbons && !usesBarCapsules;\n const drawerWidth = collapsed ? DRAWER_WIDTH_COLLAPSED : DRAWER_WIDTH_EXPANDED;\n const activeItem = activeMenuItemOf(menuItems, pathname);\n const sideMenuProps = {logo, wordmark, brandName, brandCaption, homePath, menuItems, activeItem, menuBadges, footer: sidebarFooter};\n // el lugar de la barra donde caen las acciones de cada pantalla (BarActions); con la barra fundida con el menú no hay\n // barra en escritorio y se quedan en la pantalla\n const [barActionsSlot, setBarActionsSlot] = useState(null);\n // en el teléfono con cápsulas no hay barra de arriba (la de pestañas va abajo): las acciones flotan arriba a la derecha\n const usesPhoneFloatingActions = usesTabBar && usesBarCapsules;\n const [phoneBarActionsSlot, setPhoneBarActionsSlot] = useState(null);\n const barActions = useMemo(() => ({\n slot: isBarMerged ? null : barActionsSlot,\n phoneSlot: usesPhoneFloatingActions ? phoneBarActionsSlot : null,\n usesCapsules: usesBarCapsules,\n }), [isBarMerged, barActionsSlot, usesPhoneFloatingActions, phoneBarActionsSlot, usesBarCapsules]);\n\n return (\n <BarActionsSlotContext.Provider value={barActions}>\n <Box sx={[{display: 'flex', flexDirection: 'column', minHeight: '100vh', bgcolor: 'background.default'}, isGlass && ambientBackgroundOf]}>\n <SkipToContentLink />\n <GlassLensFilter />\n\n {/* en cápsulas, lo que pasa por debajo de la barra se desenfoca en la orilla: debajo del menú lateral, que sigue nítido */}\n {isGlass && usesBarCapsules && (\n <Box aria-hidden=\"true\"\n sx={(theme) => ({\n ...scrollEdgeStylesOf(theme), display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'block'},\n position: 'fixed', top: 0, left: 0, right: 0, height: `calc(${APP_BAR_HEIGHT} + ${panelGap}px)`,\n zIndex: theme.zIndex.drawer - 1, pointerEvents: 'none',\n })} />\n )}\n {/* en cápsulas con vidrio la barra va transparente (color=\"transparent\": el tema no le pone vidrio) y cada grupo\n lleva el suyo; con el minimalista se queda del color del fondo, que tapa lo que pasa por debajo */}\n <AppBar ref={appBarRef} position=\"sticky\" color={isGlass && usesBarCapsules ? 'transparent' : undefined}\n sx={(theme) => ({\n pt: 'env(safe-area-inset-top)',\n // encima del menú lateral, como en el DashboardLayout de MUI\n zIndex: theme.zIndex.drawer + 1,\n // con vidrio flota: en escritorio arriba, separada de las orillas; en el teléfono ABAJO, en una cápsula,\n // como la barra de Safari en el iPhone (en el teléfono se veía cuadrada y pegada arriba; lo reportaron).\n // Abajo se suma la zona del gesto de inicio del iPhone (safe-area) a la separación\n ...(isGlass && {\n // en cápsulas no se recorta: la sombra de cada cápsula se saldría cortada\n overflow: usesBarCapsules ? 'visible' : 'hidden',\n [DESKTOP_MEDIA_QUERY]: {\n top: panelGap, mt: `${panelGap}px`, mx: `${panelGap}px`, width: 'auto',\n borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`,\n // sin la raya de abajo de la barra fija: no hay franja que separar\n ...(usesBarCapsules && {borderBottom: 0, boxShadow: 'none'}),\n ...(isBarMerged && {display: 'none'}),\n },\n }),\n // el minimalista en cápsulas no flota (su barra es del color del fondo y tapa lo que pasa por debajo):\n // la separación de las orillas va por dentro, para que las cápsulas queden donde las del vidrio\n ...(!isGlass && usesBarCapsules && {\n [DESKTOP_MEDIA_QUERY]: {pt: `calc(env(safe-area-inset-top) + ${panelGap}px)`, px: `${panelGap}px`},\n }),\n // en el teléfono la barra de pestañas (MobileTabBar) la reemplaza: con ella van el menú y la cuenta\n ...(usesTabBar && {[PHONE_MEDIA_QUERY]: {display: 'none'}}),\n })}>\n {/* a la izquierda, la ☰ queda alineada con los íconos del menú lateral (los dos a 32 px de su orilla) */}\n <Toolbar disableGutters sx={{minHeight: {xs: APP_BAR_HEIGHT_PIXELS, sm: APP_BAR_HEIGHT_PIXELS}, gap: 1, pl: {xs: 1.5, md: isSideMenu ? 1.5 : 3}, pr: {xs: 1.5, md: 3}}}>\n {/* la ☰ del teléfono va en la primera cápsula (en escritorio la de encoger el menú y el nombre). OJO: dentro de\n una cápsula, sin el margen negativo de «orilla de inicio» de MUI (-12 px): el botón quedaba pegado a la\n curva. Así su círculo queda concéntrico con la cápsula */}\n <BarGroup isCapsule={usesBarCapsules}>\n <IconButton edge=\"start\" aria-label={t('drawer.open')} aria-expanded={isMobileMenuOpen} onClick={() => setIsMobileMenuOpen(true)}\n sx={{display: 'inline-flex', [DESKTOP_MEDIA_QUERY]: {display: 'none'}, color: 'inherit', ...((isGlass || usesBarCapsules) && {ml: 0})}}>\n <MenuIcon />\n </IconButton>\n {isSideMenu && <CollapseMenuButton collapsed={collapsed} onToggle={toggleCollapsed} />}\n <BarBrand logo={logo} wordmark={wordmark} brandName={brandName} brandCaption={brandCaption} homePath={homePath}\n sx={isSideMenu ? {display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'flex'}} : {display: 'flex'}} />\n </BarGroup>\n {isSideMenu ? (\n <BarTitle brandName={brandName} />\n ) : (\n <>\n {/* el menú de arriba no sale en el teléfono: su cápsula tampoco (vacía se vería) */}\n <BarGroup isCapsule={usesBarCapsules} sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'flex'}}}>\n <TopMenu menuItems={menuItems} activeItem={activeItem} />\n </BarGroup>\n <Box sx={{flexGrow: 1}} />\n </>\n )}\n {/* las acciones de la pantalla abierta (BarActions, y las de PageHeading con cápsulas): cada grupo cae aquí,\n antes de la cuenta. Solo en escritorio: en el teléfono flotan arriba (phoneBarActionsSlot, en el contenido) */}\n <Box ref={setBarActionsSlot} sx={{display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'flex'}, alignItems: 'center', gap: 1, minWidth: 0}} />\n <BarGroup isCapsule={usesBarCapsules}>\n <HeaderTools headerActions={headerActions} accountSettings={accountSettings} showFontSizeToggle={showFontSizeToggle} showLanguageToggle={showLanguageToggle}\n showColorSchemeToggle={showColorSchemeToggle} user={user} profilePath={profilePath} accountMenuItems={accountMenuItems}\n onSignOut={onSignOut} isSigningOut={isSigningOut} />\n </BarGroup>\n </Toolbar>\n {ribbons}\n {/* con la barra de pestañas el aviso de versión nueva flota aparte, en su cápsula (en el teléfono esta barra no\n se ve); sin ella, franja dentro de la barra */}\n {!usesTabBar && <NewBuildNotice />}\n {isNavigating && (\n <LinearProgress aria-label={t('navigating')} color=\"inherit\"\n sx={{position: 'absolute', bottom: 0, left: 0, right: 0, height: NAVIGATING_BAR_PIXELS}} />\n )}\n </AppBar>\n {usesTabBar && <NewBuildNotice variant={NEW_BUILD_NOTICE_VARIANT.floating} />}\n {usesTabBar && (\n <MobileTabBar menuItems={menuItems} activeItem={activeItem} menuBadges={menuBadges} moreFooter={sidebarFooter} minimizeOnScroll={minimizeTabBarOnScroll}\n onPinnedTabsChange={onMobileTabsChange}\n account={(\n <HeaderTools headerActions={headerActions} accountSettings={accountSettings} showFontSizeToggle={showFontSizeToggle}\n showLanguageToggle={showLanguageToggle} showColorSchemeToggle={showColorSchemeToggle} user={user}\n profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut} isSigningOut={isSigningOut} />\n )} />\n )}\n\n <Box sx={{display: 'flex', flexGrow: 1, minWidth: 0}}>\n {isSideMenu && (\n <Drawer variant=\"permanent\"\n sx={(theme) => ({\n display: 'none', [DESKTOP_MEDIA_QUERY]: {display: 'block'}, width: drawerWidth + panelGap, flexShrink: 0, whiteSpace: 'nowrap',\n transition: drawerTransitionOf(theme),\n '& .MuiDrawer-paper': {\n width: drawerWidth, left: panelGap, boxSizing: 'border-box', overflowX: 'hidden', transition: drawerTransitionOf(theme, ['width']),\n // justo debajo de la barra (su alto real, con avisos o sin ellos) y hasta abajo; si la barra se\n // fundió con el menú, de arriba a abajo\n ...(isBarMerged\n ? {top: panelGap, height: `calc(100% - ${panelGap * 2}px)`}\n : {top: `calc(${APP_BAR_HEIGHT} + ${panelGap}px)`, height: `calc(100% - ${APP_BAR_HEIGHT} - ${panelGap * 2}px)`}),\n // el panel flotante: separado de las orillas y sin la línea de la derecha (lo separa su brillo).\n // Encogido es una cápsula (radio de la mitad de su ancho), como las barras de pestañas de vidrio\n ...(isGlass && {\n border: 0,\n borderRadius: `${GLASS_PANEL_RADIUS_PIXELS}px`,\n }),\n },\n })}>\n <SideMenu {...sideMenuProps} showBrand={false} collapsed={collapsed}\n railTop={isBarMerged ? (\n <>\n <CollapseMenuButton collapsed={collapsed} onToggle={toggleCollapsed} />\n {logo && <RailBrand logo={logo} brandName={brandName} homePath={homePath} />}\n </>\n ) : undefined}\n railBottom={isBarMerged ? (\n <HeaderTools vertical headerActions={headerActions} accountSettings={accountSettings} showFontSizeToggle={showFontSizeToggle}\n showLanguageToggle={showLanguageToggle} showColorSchemeToggle={showColorSchemeToggle} user={user}\n profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut}\n isSigningOut={isSigningOut} />\n ) : undefined} />\n </Drawer>\n )}\n\n <Box component=\"main\" id={MAIN_CONTENT_ID} tabIndex={-1}\n sx={(theme) => ({\n flexGrow: 1, minWidth: 0, outline: 'none', px: {xs: 2, md: 3, lg: 4}, pt: 3, pb: 6,\n // con la barra de pestañas abajo (teléfono): arriba, lugar para la hora y la cámara del iPhone; abajo,\n // lugar para que la barra (que flota a GLASS_PANEL_GAP_PIXELS) no tape el final de la página\n ...(usesTabBar && {\n [PHONE_MEDIA_QUERY]: {\n pt: `calc(${theme.spacing(3)} + env(safe-area-inset-top))`,\n pb: `calc(${MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait + GLASS_PANEL_GAP_PIXELS * 2}px + env(safe-area-inset-bottom) + ${theme.spacing(3)} + var(${FLOATING_BOTTOM_SPACE_CSS_VARIABLE}, 0px))`,\n },\n }),\n })}>\n {/* teléfono con cápsulas: el renglón de las acciones de la pantalla, arriba a la derecha. Va pegado (sticky)\n y no fijo: así ocupa su lugar arriba de la página (no tapa las migas ni el título) y al bajar se queda\n flotando. Vacío no se ve. Solo las cápsulas reciben clics: el hueco de la izquierda deja pasar el dedo\n a lo de abajo */}\n {usesPhoneFloatingActions && (\n <Box ref={setPhoneBarActionsSlot} data-phone-bar-actions=\"\"\n sx={(theme) => ({\n display: 'none',\n [PHONE_MEDIA_QUERY]: {\n display: 'flex', '&:empty': {display: 'none'},\n position: 'sticky', top: `calc(env(safe-area-inset-top) + ${GLASS_PANEL_GAP_PIXELS}px)`, zIndex: theme.zIndex.appBar,\n justifyContent: 'flex-end', alignItems: 'center', gap: 1, mb: 2,\n pointerEvents: 'none', '& > *': {pointerEvents: 'auto'},\n },\n })} />\n )}\n <Box sx={{maxWidth: contentMaxWidth, mx: 'auto'}}>{children}</Box>\n </Box>\n </Box>\n\n {/* el cajón del teléfono (los dos estilos): con el logo arriba, se cierra al elegir una pantalla */}\n <Drawer variant=\"temporary\" open={isMobileMenuOpen} onClose={() => setIsMobileMenuOpen(false)} ModalProps={{keepMounted: true}}\n sx={{[DESKTOP_MEDIA_QUERY]: {display: 'none'}}}\n slotProps={{paper: {sx: {width: DRAWER_WIDTH_EXPANDED, boxSizing: 'border-box', pt: 'env(safe-area-inset-top)', pb: 'env(safe-area-inset-bottom)'}}}}>\n <SideMenu {...sideMenuProps} collapsed={false} onNavigate={() => setIsMobileMenuOpen(false)} />\n </Drawer>\n </Box>\n </BarActionsSlotContext.Provider>\n );\n};\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} [props.logo] el logo chico (28 px), en la barra junto al nombre y en el cajón del teléfono\n * @param {import('react').ReactNode} [props.wordmark] el logo ancho, la marca con su nombre en una sola imagen (como el de muchas\n * empresas): va en lugar del logo y el nombre en la barra y arriba del menú del teléfono, a 24 px de alto. Con el menú\n * encogido se queda el logo chico (logo), si lo hay. brandName sigue siendo su nombre para el lector de pantalla\n * @param {string} [props.brandName] el nombre de la app; por omisión el appName de ToolkitProvider\n * @param {string} [props.brandCaption] el renglón chico debajo del nombre («Panel de administración»)\n * @param {string} [props.homePath] por omisión '/'\n * @param {object[]} [props.menuItems] lo que regresa composeMenu\n * @param {'side'|'top'} [props.menuPosition] por omisión 'side' (MENU_POSITION)\n * @param {{name: string, email?: string, subtitle?: string, avatarUrl?: string}|null} [props.user] quién está dentro\n * @param {string} [props.profilePath] la ruta de Mi perfil\n * @param {object[]} [props.accountMenuItems] renglones propios en el menú de la cuenta ({to | onClick, label | labelKey, Icon})\n * @param {() => void} [props.onSignOut] sin él no sale «Salir» (una app sin sesión)\n * @param {boolean} [props.isSigningOut]\n * @param {import('react').ReactNode} [props.headerActions] botones propios en la barra, antes de la cuenta\n * @param {Record<string, number>} [props.menuBadges] los conteos del servidor por badgeKey de cada entrada del menú\n * (useMenuBadges): lo pendiente de cada pantalla, como numerito\n * @param {import('react').ReactNode} [props.accountSettings] ajustes propios del proyecto dentro del menú de la cuenta, debajo de\n * letra, idioma y claro u oscuro (con SettingSection, para que se vean igual)\n * @param {import('react').ReactNode} [props.sidebarFooter] lo de abajo del menú lateral (una tarjeta de ayuda…)\n * @param {boolean} [props.showFontSizeToggle] por omisión sí\n * @param {boolean} [props.showLanguageToggle] por omisión sí\n * @param {boolean} [props.showColorSchemeToggle] por omisión sí\n * @param {import('react').ReactNode} [props.ribbons] avisos propios dentro de la barra\n * @param {boolean} [props.isNavigating] la pantalla siguiente se está descargando (react-router: useIsNavigating)\n * @param {boolean} [props.defaultCollapsed] el menú lateral empieza encogido (la persona lo cambia y se recuerda)\n * @param {boolean} [props.minimizeTabBarOnScroll] en el teléfono con vidrio, la barra de pestañas se encoge al leer hacia\n * abajo y regresa al subir (como iOS 26); por omisión sí\n * @param {number|string} [props.contentMaxWidth] por omisión 1400\n * @param {(collapsed: boolean) => void} [props.onMenuCollapse] la persona encogió (true) o abrió (false) el menú lateral\n * @param {(isOpen: boolean) => void} [props.onMobileMenuChange] se abrió o se cerró el menú del teléfono (el cajón)\n * @param {(keys: string[]|null) => void} [props.onMobileTabsChange] la persona eligió sus pestañas del teléfono (las llaves\n * de menuItemKeyOf; null: regresó a las de omisión). Se recuerdan en el navegador; con esto el proyecto las puede guardar\n * en su servidor\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete (para todo el marco)\n * @param {import('react').ReactNode} props.children el contenido\n */\nexport const AppLayout = ({\n logo, wordmark, brandName, brandCaption, homePath = '/', menuItems = [], menuPosition = MENU_POSITION.side, user = null, profilePath, accountMenuItems = [], onSignOut,\n isSigningOut = false, headerActions, accountSettings, menuBadges, sidebarFooter, showFontSizeToggle = true, showLanguageToggle = true, showColorSchemeToggle = true, ribbons,\n isNavigating = false, defaultCollapsed = false, minimizeTabBarOnScroll = true, contentMaxWidth = 1400, onMenuCollapse, onMobileMenuChange, onMobileTabsChange,\n texts, children,\n}) => {\n const {appName} = useToolkitSettings();\n // un ícono ya pintado (icon) se vuelve componente aquí, una vez: las piezas del menú pintan siempre Icon\n const shownMenuItems = useMemo(() => menuItemsWithIconsOf(menuItems), [menuItems]);\n const appMenu = useMemo(() => ({menuItems: shownMenuItems}), [shownMenuItems]);\n // los textos del marco llegan a todas sus piezas (la cuenta, el menú, las pestañas del teléfono), cada una lee los suyos\n return (\n <ComponentTexts namespace=\"layout\" texts={texts}>\n <PageTitleProvider>\n <AppMenuContext.Provider value={appMenu}>\n <AppLayoutFrame logo={logo} wordmark={wordmark} brandName={brandName ?? appName} brandCaption={brandCaption} homePath={homePath} menuItems={shownMenuItems} menuPosition={menuPosition}\n user={user} profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut} isSigningOut={isSigningOut}\n headerActions={headerActions} accountSettings={accountSettings} menuBadges={menuBadges} sidebarFooter={sidebarFooter} showFontSizeToggle={showFontSizeToggle}\n showLanguageToggle={showLanguageToggle} showColorSchemeToggle={showColorSchemeToggle} ribbons={ribbons}\n isNavigating={isNavigating} defaultCollapsed={defaultCollapsed} minimizeTabBarOnScroll={minimizeTabBarOnScroll}\n contentMaxWidth={contentMaxWidth} onMenuCollapse={onMenuCollapse} onMobileMenuChange={onMobileMenuChange}\n onMobileTabsChange={onMobileTabsChange}>\n {children}\n </AppLayoutFrame>\n </AppMenuContext.Provider>\n </PageTitleProvider>\n </ComponentTexts>\n );\n};\n"],"names":["theme"],"mappings":"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"}
@@ -36,13 +36,15 @@ function HeaderTools({
36
36
  isSigningOut,
37
37
  settings
38
38
  }
39
- ) : settings && /* @__PURE__ */ jsx(
39
+ ) : (settings || accountMenuItems?.length > 0) && // sin sesión: el engrane con los ajustes y las ligas propias (en un sistema que ya existía, «Regresar» se perdía)
40
+ /* @__PURE__ */ jsx(
40
41
  SettingsMenu,
41
42
  {
42
43
  showFontSize: showFontSizeToggle,
43
44
  showLanguage: showLanguageToggle,
44
45
  showColorScheme: showColorSchemeToggle,
45
- extraSettings: accountSettings
46
+ extraSettings: accountSettings,
47
+ menuLinks: accountMenuItems
46
48
  }
47
49
  )
48
50
  ] });
@@ -1 +1 @@
1
- {"version":3,"file":"HeaderTools.js","sources":["../../src/appShell/HeaderTools.jsx"],"sourcesContent":["// Lo de la derecha de la barra, igual en los dos estilos de barra (menú lateral o arriba):\n// - lo que el proyecto agregue (headerActions: un botón de «reporte urgente», un buscador…);\n// - la cuenta (AccountMenu), y DENTRO de su menú los ajustes de la app: tamaño de letra, idioma, claro u oscuro y los del\n// proyecto (accountSettings). Antes iban sueltos en la barra, cada uno con su botón: ocupaban lugar y en el menú lateral\n// encogido (que hace de barra) tapaban el menú (lo reportaron; el autor pidió «un solo menú como el de perfil»);\n// - sin sesión no hay cuenta: los ajustes van detrás del engrane (SettingsMenu).\n// En columna (vertical) van abajo del menú lateral encogido con vidrio, que hace también de barra (AppLayout).\n//\n// Los botones toman el color de la barra (color: inherit): sobre la barra naranja de Stackcraft van claros, sobre una\n// barra blanca van oscuros, sin que cada botón sepa de qué color es la barra.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Box from '@mui/material/Box';\nimport Divider from '@mui/material/Divider';\n\nimport AccountMenu from '@/appShell/AccountMenu.jsx';\nimport SettingsMenu from '@/appShell/SettingsMenu.jsx';\nimport AppearanceSettings from '@/appShell/AppearanceSettings.jsx';\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} [props.headerActions]\n * @param {boolean} props.showFontSizeToggle\n * @param {boolean} props.showLanguageToggle\n * @param {boolean} props.showColorSchemeToggle\n * @param {object|null} props.user\n * @param {string} [props.profilePath]\n * @param {object[]} [props.accountMenuItems]\n * @param {import('react').ReactNode} [props.accountSettings] los ajustes del proyecto, en el menú de la cuenta (SettingSection)\n * @param {boolean} [props.vertical] en columna, para el menú lateral encogido\n * @param {() => void} [props.onSignOut]\n * @param {boolean} [props.isSigningOut]\n */\nexport default function HeaderTools({\n headerActions, showFontSizeToggle, showLanguageToggle, showColorSchemeToggle, user, profilePath, accountMenuItems, onSignOut, isSigningOut,\n accountSettings, vertical = false,\n}) {\n const hasAppearance = showFontSizeToggle || showLanguageToggle || showColorSchemeToggle;\n const settings = hasAppearance || accountSettings ? (\n <>\n <AppearanceSettings showFontSize={showFontSizeToggle} showLanguage={showLanguageToggle} showColorScheme={showColorSchemeToggle} />\n {hasAppearance && accountSettings && <Divider sx={{my: 0.5}} />}\n {accountSettings}\n </>\n ) : null;\n\n return (\n <Box sx={{display: 'flex', flexDirection: vertical ? 'column' : 'row', alignItems: 'center', gap: 0.5, flexShrink: 0, '& .MuiButtonBase-root': {color: 'inherit'}}}>\n {headerActions}\n {user ? (\n <AccountMenu user={user} profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut} isSigningOut={isSigningOut}\n settings={settings} />\n ) : settings && (\n <SettingsMenu showFontSize={showFontSizeToggle} showLanguage={showLanguageToggle} showColorScheme={showColorSchemeToggle}\n extraSettings={accountSettings} />\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAiCA,SAAwB,YAAY;AAAA,EAChC;AAAA,EAAe;AAAA,EAAoB;AAAA,EAAoB;AAAA,EAAuB;AAAA,EAAM;AAAA,EAAa;AAAA,EAAkB;AAAA,EAAW;AAAA,EAC9H;AAAA,EAAiB,WAAW;AAChC,GAAG;AACC,QAAM,gBAAgB,sBAAsB,sBAAsB;AAClE,QAAM,WAAW,iBAAiB,kBAC9B,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,sBAAmB,cAAc,oBAAoB,cAAc,oBAAoB,iBAAiB,uBAAuB;AAAA,IAC/H,iBAAiB,mBAAmB,oBAAC,SAAA,EAAQ,IAAI,EAAC,IAAI,OAAM;AAAA,IAC5D;AAAA,EAAA,EAAA,CACL,IACA;AAEJ,SACI,qBAAC,OAAI,IAAI,EAAC,SAAS,QAAQ,eAAe,WAAW,WAAW,OAAO,YAAY,UAAU,KAAK,KAAK,YAAY,GAAG,yBAAyB,EAAC,OAAO,YAAS,GAC3J,UAAA;AAAA,IAAA;AAAA,IACA,OACG;AAAA,MAAC;AAAA,MAAA;AAAA,QAAY;AAAA,QAAY;AAAA,QAA0B;AAAA,QAAoC;AAAA,QAAsB;AAAA,QAChG;AAAA,MAAA;AAAA,IAAA,IACb,YACA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAa,cAAc;AAAA,QAAoB,cAAc;AAAA,QAAoB,iBAAiB;AAAA,QACrF,eAAe;AAAA,MAAA;AAAA,IAAA;AAAA,EAAiB,GAEtD;AAER;"}
1
+ {"version":3,"file":"HeaderTools.js","sources":["../../src/appShell/HeaderTools.jsx"],"sourcesContent":["// Lo de la derecha de la barra, igual en los dos estilos de barra (menú lateral o arriba):\n// - lo que el proyecto agregue (headerActions: un botón de «reporte urgente», un buscador…);\n// - la cuenta (AccountMenu), y DENTRO de su menú los ajustes de la app: tamaño de letra, idioma, claro u oscuro y los del\n// proyecto (accountSettings). Antes iban sueltos en la barra, cada uno con su botón: ocupaban lugar y en el menú lateral\n// encogido (que hace de barra) tapaban el menú (lo reportaron; el autor pidió «un solo menú como el de perfil»);\n// - sin sesión no hay cuenta: los ajustes van detrás del engrane (SettingsMenu).\n// En columna (vertical) van abajo del menú lateral encogido con vidrio, que hace también de barra (AppLayout).\n//\n// Los botones toman el color de la barra (color: inherit): sobre la barra naranja de Stackcraft van claros, sobre una\n// barra blanca van oscuros, sin que cada botón sepa de qué color es la barra.\n//\n// Genérico del paquete: function (regla de la casa).\nimport Box from '@mui/material/Box';\nimport Divider from '@mui/material/Divider';\n\nimport AccountMenu from '@/appShell/AccountMenu.jsx';\nimport SettingsMenu from '@/appShell/SettingsMenu.jsx';\nimport AppearanceSettings from '@/appShell/AppearanceSettings.jsx';\n\n/**\n * @param {object} props\n * @param {import('react').ReactNode} [props.headerActions]\n * @param {boolean} props.showFontSizeToggle\n * @param {boolean} props.showLanguageToggle\n * @param {boolean} props.showColorSchemeToggle\n * @param {object|null} props.user\n * @param {string} [props.profilePath]\n * @param {object[]} [props.accountMenuItems]\n * @param {import('react').ReactNode} [props.accountSettings] los ajustes del proyecto, en el menú de la cuenta (SettingSection)\n * @param {boolean} [props.vertical] en columna, para el menú lateral encogido\n * @param {() => void} [props.onSignOut]\n * @param {boolean} [props.isSigningOut]\n */\nexport default function HeaderTools({\n headerActions, showFontSizeToggle, showLanguageToggle, showColorSchemeToggle, user, profilePath, accountMenuItems, onSignOut, isSigningOut,\n accountSettings, vertical = false,\n}) {\n const hasAppearance = showFontSizeToggle || showLanguageToggle || showColorSchemeToggle;\n const settings = hasAppearance || accountSettings ? (\n <>\n <AppearanceSettings showFontSize={showFontSizeToggle} showLanguage={showLanguageToggle} showColorScheme={showColorSchemeToggle} />\n {hasAppearance && accountSettings && <Divider sx={{my: 0.5}} />}\n {accountSettings}\n </>\n ) : null;\n\n return (\n <Box sx={{display: 'flex', flexDirection: vertical ? 'column' : 'row', alignItems: 'center', gap: 0.5, flexShrink: 0, '& .MuiButtonBase-root': {color: 'inherit'}}}>\n {headerActions}\n {user ? (\n <AccountMenu user={user} profilePath={profilePath} accountMenuItems={accountMenuItems} onSignOut={onSignOut} isSigningOut={isSigningOut}\n settings={settings} />\n ) : (settings || accountMenuItems?.length > 0) && (\n // sin sesión: el engrane con los ajustes y las ligas propias (en un sistema que ya existía, «Regresar» se perdía)\n <SettingsMenu showFontSize={showFontSizeToggle} showLanguage={showLanguageToggle} showColorScheme={showColorSchemeToggle}\n extraSettings={accountSettings} menuLinks={accountMenuItems} />\n )}\n </Box>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAiCA,SAAwB,YAAY;AAAA,EAChC;AAAA,EAAe;AAAA,EAAoB;AAAA,EAAoB;AAAA,EAAuB;AAAA,EAAM;AAAA,EAAa;AAAA,EAAkB;AAAA,EAAW;AAAA,EAC9H;AAAA,EAAiB,WAAW;AAChC,GAAG;AACC,QAAM,gBAAgB,sBAAsB,sBAAsB;AAClE,QAAM,WAAW,iBAAiB,kBAC9B,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,sBAAmB,cAAc,oBAAoB,cAAc,oBAAoB,iBAAiB,uBAAuB;AAAA,IAC/H,iBAAiB,mBAAmB,oBAAC,SAAA,EAAQ,IAAI,EAAC,IAAI,OAAM;AAAA,IAC5D;AAAA,EAAA,EAAA,CACL,IACA;AAEJ,SACI,qBAAC,OAAI,IAAI,EAAC,SAAS,QAAQ,eAAe,WAAW,WAAW,OAAO,YAAY,UAAU,KAAK,KAAK,YAAY,GAAG,yBAAyB,EAAC,OAAO,YAAS,GAC3J,UAAA;AAAA,IAAA;AAAA,IACA,OACG;AAAA,MAAC;AAAA,MAAA;AAAA,QAAY;AAAA,QAAY;AAAA,QAA0B;AAAA,QAAoC;AAAA,QAAsB;AAAA,QAChG;AAAA,MAAA;AAAA,IAAA,KACZ,YAAY,kBAAkB,SAAS;AAAA,IAExC;AAAA,MAAC;AAAA,MAAA;AAAA,QAAa,cAAc;AAAA,QAAoB,cAAc;AAAA,QAAoB,iBAAiB;AAAA,QACrF,eAAe;AAAA,QAAiB,WAAW;AAAA,MAAA;AAAA,IAAA;AAAA,EAAkB,GAEnF;AAER;"}
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
+ import ListItemIcon from "@mui/material/ListItemIcon";
4
+ import ListItemText from "@mui/material/ListItemText";
5
+ import MenuItem from "@mui/material/MenuItem";
6
+ import { menuItemLabelOf } from "./menuItemText.js";
7
+ import { useToolkitNavigation } from "../toolkit/toolkitContext.js";
8
+ import { useToolkitTranslation } from "../toolkit/useToolkitTranslation.js";
9
+ function MenuLinks({ links, onClose }) {
10
+ const { t } = useToolkitTranslation("layout");
11
+ const { Link: RouterLink } = useToolkitNavigation();
12
+ return links.map((menuLink) => {
13
+ const Icon = menuLink.Icon;
14
+ const linkProps = menuLink.to ? { component: RouterLink, to: menuLink.to } : menuLink.href ? { component: "a", href: menuLink.href } : {};
15
+ const label = menuItemLabelOf(menuLink, t);
16
+ return /* @__PURE__ */ jsxs(
17
+ MenuItem,
18
+ {
19
+ ...linkProps,
20
+ onClick: () => {
21
+ onClose();
22
+ menuLink.onClick?.();
23
+ },
24
+ children: [
25
+ (Icon || menuLink.icon) && /* @__PURE__ */ jsx(ListItemIcon, { children: Icon ? /* @__PURE__ */ jsx(Icon, { fontSize: "small" }) : menuLink.icon }),
26
+ /* @__PURE__ */ jsx(ListItemText, { primary: label })
27
+ ]
28
+ },
29
+ menuLink.to ?? menuLink.href ?? label
30
+ );
31
+ });
32
+ }
33
+ export {
34
+ MenuLinks as default
35
+ };
36
+ //# sourceMappingURL=MenuLinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MenuLinks.js","sources":["../../src/appShell/MenuLinks.jsx"],"sourcesContent":["// Las ligas propias de la app en el menú de la cuenta o en el del engrane (accountMenuItems): Mi perfil, Facturación,\n// «Regresar al sistema»… Una liga de la app (to) navega con el router; una de otro sistema (href) es una dirección normal;\n// sin ninguna de las dos, solo hace lo suyo (onClick). El ícono es un componente (Icon) o uno ya pintado (icon).\n//\n// Una sola pieza para los dos menús: sin sesión las ligas siguen a la mano detrás del engrane (en un sistema que ya existía,\n// «Regresar» se perdía porque solo lo pintaba la cuenta).\n//\n// Genérico del paquete: function (regla de la casa).\nimport ListItemIcon from '@mui/material/ListItemIcon';\nimport ListItemText from '@mui/material/ListItemText';\nimport MenuItem from '@mui/material/MenuItem';\n\nimport {menuItemLabelOf} from '@/appShell/menuItemText.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/**\n * @param {object} props\n * @param {Array<{to?: string, href?: string, onClick?: Function, label?: string, labelKey?: string, Icon?: import('react').ElementType, icon?: import('react').ReactNode}>} props.links\n * @param {() => void} props.onClose cierra el menú al elegir\n */\nexport default function MenuLinks({links, onClose}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n return links.map((menuLink) => {\n const Icon = menuLink.Icon;\n const linkProps = menuLink.to ? {component: RouterLink, to: menuLink.to} : menuLink.href ? {component: 'a', href: menuLink.href} : {};\n const label = menuItemLabelOf(menuLink, t);\n return (\n <MenuItem key={menuLink.to ?? menuLink.href ?? label} {...linkProps}\n onClick={() => {\n onClose();\n menuLink.onClick?.();\n }}>\n {(Icon || menuLink.icon) && <ListItemIcon>{Icon ? <Icon fontSize=\"small\" /> : menuLink.icon}</ListItemIcon>}\n <ListItemText primary={label} />\n </MenuItem>\n );\n });\n}\n"],"names":[],"mappings":";;;;;;;;AAqBA,SAAwB,UAAU,EAAC,OAAO,WAAU;AAChD,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,EAAC,MAAM,WAAA,IAAc,qBAAA;AAC3B,SAAO,MAAM,IAAI,CAAC,aAAa;AAC3B,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,SAAS,KAAK,EAAC,WAAW,YAAY,IAAI,SAAS,GAAA,IAAM,SAAS,OAAO,EAAC,WAAW,KAAK,MAAM,SAAS,KAAA,IAAQ,CAAA;AACnI,UAAM,QAAQ,gBAAgB,UAAU,CAAC;AACzC,WACI;AAAA,MAAC;AAAA,MAAA;AAAA,QAAsD,GAAG;AAAA,QAChD,SAAS,MAAM;AACX,kBAAA;AACA,mBAAS,UAAA;AAAA,QACb;AAAA,QACJ,UAAA;AAAA,WAAA,QAAQ,SAAS,SAAS,oBAAC,cAAA,EAAc,UAAA,OAAO,oBAAC,MAAA,EAAK,UAAS,QAAA,CAAQ,IAAK,SAAS,MAAK;AAAA,UAC5F,oBAAC,cAAA,EAAa,SAAS,MAAA,CAAO;AAAA,QAAA;AAAA,MAAA;AAAA,MANnB,SAAS,MAAM,SAAS,QAAQ;AAAA,IAAA;AAAA,EASvD,CAAC;AACL;"}
@@ -9,7 +9,7 @@ import Drawer from "@mui/material/Drawer";
9
9
  import Typography from "@mui/material/Typography";
10
10
  import useMediaQuery from "@mui/material/useMediaQuery";
11
11
  import MoreHorizRoundedIcon from "@mui/icons-material/MoreHorizRounded";
12
- import { menuItemLabelOf } from "./menuItemText.js";
12
+ import { menuLinkPropsOf, menuItemLabelOf } from "./menuItemText.js";
13
13
  import { PHONE_LANDSCAPE_MEDIA_QUERY, MOBILE_TAB_BAR_HEIGHT_PIXELS, GLASS_PANEL_GAP_PIXELS, PHONE_MEDIA_QUERY } from "./shellSizes.js";
14
14
  import SideMenu from "./SideMenu.js";
15
15
  import SubmenuActionsMenu, { SUBMENU_PLACEMENT } from "./SubmenuActionsMenu.js";
@@ -221,10 +221,10 @@ function MobileTabBar({ menuItems, activeItem, menuBadges, account, moreFooter,
221
221
  isActive: isInside(menuItem),
222
222
  badge: menuBadgeOf(menuItem, menuBadges),
223
223
  hasPopup: hasChildren,
224
- linkProps: hasChildren ? {} : { component: RouterLink, to: menuItem.to },
224
+ linkProps: hasChildren ? {} : menuLinkPropsOf(menuItem, RouterLink),
225
225
  onClick: hasChildren ? (event) => setSubmenu({ anchorElement: event.currentTarget, menuItem }) : void 0
226
226
  },
227
- menuItem.to ?? menuItemLabelOf(menuItem, t)
227
+ menuItem.to ?? menuItem.href ?? menuItemLabelOf(menuItem, t)
228
228
  );
229
229
  }),
230
230
  more.length > 0 && /* @__PURE__ */ jsx(
@@ -1 +1 @@
1
- {"version":3,"file":"MobileTabBar.js","sources":["../../src/appShell/MobileTabBar.jsx"],"sourcesContent":["// La barra de pestañas del teléfono con vidrio, como la de iOS 26: una cápsula de vidrio flotando abajo con las secciones\n// principales (mobileTabsOf: las que caben a lo ancho, y «Más» con el resto), y aparte, en su círculo de vidrio, la cuenta\n// (con todos los ajustes adentro). La reemplaza a la barra de abajo con ☰ y al cajón del menú (lo pidió el autor: «estilo\n// Apple pero con sus submenús de acciones»). También va con el minimalista cuando la barra va en cápsulas (usesMobileTabBar), sólida y\n// del gris de lo que flota (minimalFloatingStylesOf).\n//\n// - Una pestaña con submenú no navega: abre el menú de acciones de Apple arriba de ella (SubmenuActionsMenu).\n// - «Más» abre una hoja desde abajo con el resto del menú, por secciones (SideMenu, el mismo del escritorio). Arriba trae\n// «Editar la barra»: cada quien elige qué pantallas van abajo (TabBarEditor; se recuerda con usePinnedMobileTabs),\n// como pide la guía de Apple. Sin elegir, van las que marca el proyecto (mobileTab en menu.js).\n// - Cada pestaña lleva su badge como los de Apple (HIG «Tab bars»): ROJO con el número en blanco y solo para conteos (lo\n// pendiente, del servidor); un badge de texto («Nuevo») no va en la pestaña, se queda en el menú: Apple los reserva para\n// lo que pide atención.\n// - Cuántas pestañas van se mide (useFittingTabCount): las que caben con al menos MIN_TAB_PIXELS cada una; las demás van\n// a «Más». En una ventana angosta salen menos y en una ancha más, y los nombres no se cortan («Compone…»).\n// - Acostado (pantalla bajita), la barra va compacta: ícono y nombre lado a lado, para dejarle el alto al contenido.\n// - Al leer hacia abajo se encoge (como iOS 26): queda una cápsula chica con la pestaña donde está la persona, y la cuenta\n// más chica; al subir un poco, o al tocar la cápsula, regresa completa (cuándo: tabBarScroll.js). Se mueve solo con\n// opacidad y escala, lo que el navegador anima sin volver a acomodar la página: en un teléfono barato no se traba.\n// Solo se ve en el teléfono (PHONE_MEDIA_QUERY); en escritorio no ocupa nada.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useLayoutEffect, useState} from 'react';\nimport Badge from '@mui/material/Badge';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Drawer from '@mui/material/Drawer';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\nimport {menuItemLabelOf} from '@/appShell/menuItemText.js';\nimport {\n GLASS_PANEL_GAP_PIXELS, MOBILE_TAB_BAR_HEIGHT_PIXELS, PHONE_LANDSCAPE_MEDIA_QUERY, PHONE_MEDIA_QUERY,\n} from '@/appShell/shellSizes.js';\nimport SideMenu from '@/appShell/SideMenu.jsx';\nimport SubmenuActionsMenu, {SUBMENU_PLACEMENT} from '@/appShell/SubmenuActionsMenu.jsx';\nimport TabBarEditor from '@/appShell/TabBarEditor.jsx';\nimport {usePinnedMobileTabs} from '@/appShell/usePinnedMobileTabs.js';\nimport {useTabBarMinimized} from '@/appShell/useTabBarMinimized.js';\nimport {menuBadgeOf, menuItemKeyOf, mobileTabsOf} from '@/navigation/appMenu.js';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {accentTextStylesOf, glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {mergeStyles} from '@/theme/mergeComponents.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n// el nombre de la pestaña parado y acostado (px), lo ancho máximo de la cápsula y lo alto de la hoja de «Más»\nconst TAB_LABEL_PIXELS = Object.freeze({portrait: 10, landscape: 12});\nconst TAB_ICON_SIZE = '1.5rem';\nconst TAB_BAR_MAX_WIDTH_PIXELS = 560;\n// lo menos que mide una pestaña (px) para que su nombre se lea entero: parada, un nombre de unas 11 letras («Formularios»\n// pide 61 px, medido en Chrome); acostada, ícono y nombre lado a lado. Con menos, la pestaña se va a «Más»\nconst MIN_TAB_PIXELS = Object.freeze({portrait: 60, landscape: 104});\n// antes de medir (o si la barra no se ve): las de siempre, 4 y «Más»\nconst DEFAULT_TAB_COUNT = 5;\nconst MORE_SHEET_MAX_WIDTH_PIXELS = 640;\nconst MORE_SHEET_MAX_HEIGHT = '75vh';\nconst BADGE_MAX = 99;\n// la barra flota sobre el contenido: vidrio más opaco que el de los controles, una línea fina en la orilla y una sombra\n// debajo. Con el vidrio de siempre, en claro se perdía sobre las tarjetas blancas y no parecía flotar (lo reportaron)\nconst FLOATING_GLASS_OPACITY = 0.8;\nconst FLOATING_EDGE = Object.freeze({\n light: {outline: 'rgba(15, 23, 42, 0.12)', shadow: '0 10px 30px rgba(15, 23, 42, 0.20), 0 2px 6px rgba(15, 23, 42, 0.10)'},\n dark: {outline: 'rgba(255, 255, 255, 0.16)', shadow: '0 10px 30px rgba(0, 0, 0, 0.65), 0 2px 6px rgba(0, 0, 0, 0.4)'},\n});\n// la pestaña elegida: un tinte del acento (en claro una gota blanca sobre la cápsula blanca no se distinguía)\nconst ACTIVE_TINT_PERCENT = Object.freeze({light: 14, dark: 24});\n// encogida: lo alto de la cápsula chica (px) y a qué tamaño se va lo que desaparece\nconst MINIMIZED_PIXELS = Object.freeze({portrait: 48, landscape: 40});\nconst HIDDEN_SCALE = 0.6;\n\n/**\n * Lo que aparece o desaparece al encoger la barra: solo opacidad y escala. visibility espera a que termine la animación\n * para esconderlo, y así lo que ya no se ve tampoco se puede tocar.\n *\n * @param {object} theme\n * @param {boolean} isShown\n * @param {string} transformOrigin hacia dónde se encoge ('left bottom')\n * @returns {object}\n */\nfunction appearStylesOf(theme, isShown, transformOrigin) {\n const duration = theme.transitions.duration.short;\n return {\n transformOrigin,\n opacity: isShown ? 1 : 0,\n transform: isShown ? 'none' : `scale(${HIDDEN_SCALE})`,\n visibility: isShown ? 'visible' : 'hidden',\n transition: `${theme.transitions.create(['opacity', 'transform'], {duration})}, visibility 0s linear ${isShown ? 0 : duration}ms`,\n };\n}\n\n/**\n * La superficie de la barra y del círculo de la cuenta: vidrio más opaco, su línea fina y su sombra (la sombra va en una\n * capa detrás, ::before, para no pisar el filo de luz del vidrio, que también es una sombra). Con el minimalista, sólida.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction floatingSurfaceOf(theme) {\n // con el minimalista (barra en cápsulas) la barra es sólida: su gris de lo que flota, sin línea ni sombra\n if (!isGlassTheme(theme)) {\n return {...minimalFloatingStylesOf(theme), position: 'relative', borderRadius: '999px', height: '100%'};\n }\n return {\n ...glassStylesOf(theme, {opacity: FLOATING_GLASS_OPACITY}),\n position: 'relative',\n borderRadius: '999px',\n height: '100%',\n outline: `1px solid ${FLOATING_EDGE.light.outline}`,\n '&::before': {\n content: '\"\"', position: 'absolute', inset: 0, borderRadius: 'inherit', zIndex: -1, pointerEvents: 'none',\n boxShadow: FLOATING_EDGE.light.shadow,\n },\n ...theme.applyStyles('dark', {\n outline: `1px solid ${FLOATING_EDGE.dark.outline}`,\n '&::before': {boxShadow: FLOATING_EDGE.dark.shadow},\n }),\n };\n}\n\n/**\n * La pestaña elegida: el tinte del acento y su letra legible.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction activeTabOf(theme) {\n const primary = (theme.vars ?? theme).palette.primary.main;\n return mergeStyles({\n backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.light}%, transparent)`,\n ...theme.applyStyles('dark', {backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.dark}%, transparent)`}),\n }, accentTextStylesOf(theme));\n}\n\n/**\n * Una pestaña: liga a su pantalla o, con submenú, botón que abre el menú de acciones.\n *\n * @param {object} props\n * @param {string} props.label ya traducido\n * @param {import('react').ElementType} [props.Icon]\n * @param {boolean} props.isActive\n * @param {{content: number|string, isCount: boolean}|null} props.badge\n * @param {object} [props.linkProps] {component, to} si navega\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.hasPopup] abre un menú\n */\nfunction TabButton({label, Icon, isActive, badge, linkProps = {}, onClick, hasPopup = false}) {\n return (\n <ButtonBase {...linkProps} onClick={onClick} aria-haspopup={hasPopup ? 'menu' : undefined} aria-current={isActive ? 'page' : undefined}\n sx={(theme) => mergeStyles({\n flex: '1 1 0', minWidth: 0, height: '100%', borderRadius: '999px', px: 0,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 0.25,\n color: 'text.primary',\n transition: theme.transitions.create(['background-color', 'color'], {duration: theme.transitions.duration.shortest}),\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {flexDirection: 'row', gap: 0.75},\n }, isActive ? activeTabOf(theme) : {})}>\n <Badge invisible={!badge?.isCount} color=\"error\" max={BADGE_MAX} overlap=\"circular\" badgeContent={badge?.isCount ? badge.content : undefined}>\n {Icon ? <Icon sx={{fontSize: TAB_ICON_SIZE}} /> : <Box sx={{width: TAB_ICON_SIZE, height: TAB_ICON_SIZE}} />}\n </Badge>\n <Typography component=\"span\" noWrap\n sx={{...textSizeOf(TAB_LABEL_PIXELS.portrait), fontWeight: 500, maxWidth: '100%',\n [PHONE_LANDSCAPE_MEDIA_QUERY]: textSizeOf(TAB_LABEL_PIXELS.landscape)}}>\n {label}\n </Typography>\n </ButtonBase>\n );\n}\n\n/**\n * Cuántas pestañas caben en la cápsula (contando «Más»). Se mide la cápsula, que no cambia de ancho con sus pestañas (se\n * estira hasta su tope), así que la cuenta no da vueltas. Se vuelve a medir al girar el teléfono o cambiar la ventana.\n *\n * @param {HTMLElement|null} capsule\n * @param {number} minTabPixels lo menos que mide una pestaña\n * @returns {number}\n */\nfunction useFittingTabCount(capsule, minTabPixels) {\n const [tabCount, setTabCount] = useState(DEFAULT_TAB_COUNT);\n // antes de pintar: así no se ve un momento con las pestañas apretadas\n useLayoutEffect(() => {\n if (!capsule) {\n return undefined;\n }\n const measure = () => {\n const style = window.getComputedStyle(capsule);\n const innerWidth = capsule.clientWidth - Number.parseFloat(style.paddingLeft) - Number.parseFloat(style.paddingRight);\n // en escritorio la barra no se ve (ancho 0): se quedan las de siempre\n if (innerWidth > 0) {\n setTabCount(Math.max(2, Math.floor(innerWidth / minTabPixels)));\n }\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(capsule);\n return () => observer.disconnect();\n }, [capsule, minTabPixels]);\n return tabCount;\n}\n\n/**\n * @param {object} props\n * @param {object[]} props.menuItems lo que regresa composeMenu\n * @param {object|null} props.activeItem la entrada de la pantalla abierta\n * @param {Record<string, number>} [props.menuBadges] los conteos del servidor por badgeKey\n * @param {import('react').ReactNode} [props.account] la cuenta (o el engrane), en su círculo junto a la barra\n * @param {import('react').ReactNode} [props.moreFooter] lo que va al final de la hoja de «Más» (la tarjeta del menú lateral)\n * @param {boolean} [props.minimizeOnScroll] se encoge al leer hacia abajo; por omisión sí\n * @param {(keys: string[]|null) => void} [props.onPinnedTabsChange] la persona eligió sus pestañas (null: regresó a las de\n * omisión)\n */\nexport default function MobileTabBar({menuItems, activeItem, menuBadges, account, moreFooter, minimizeOnScroll = true, onPinnedTabsChange}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n const {pinnedKeys, setPinnedKeys} = usePinnedMobileTabs();\n const [tabsCapsule, setTabsCapsule] = useState(null);\n const isLandscape = useMediaQuery(PHONE_LANDSCAPE_MEDIA_QUERY);\n const tabCount = useFittingTabCount(tabsCapsule, isLandscape ? MIN_TAB_PIXELS.landscape : MIN_TAB_PIXELS.portrait);\n const {tabs, more} = mobileTabsOf(menuItems, pinnedKeys, tabCount);\n const [submenu, setSubmenu] = useState({anchorElement: null, menuItem: null});\n const [isMoreOpen, setIsMoreOpen] = useState(false);\n const [isEditingTabs, setIsEditingTabs] = useState(false);\n // al salir del editor su botón desaparece con el foco adentro: el foco regresa a «Editar la barra» (sin eso se perdía\n // y Escape ya no cerraba la hoja; también lo necesita quien usa lector de pantalla)\n const [returnsFromEditor, setReturnsFromEditor] = useState(false);\n const leaveEditor = () => {\n setIsEditingTabs(false);\n setReturnsFromEditor(true);\n };\n const closeMore = () => {\n setIsMoreOpen(false);\n setIsEditingTabs(false);\n setReturnsFromEditor(false);\n };\n const {isMinimized, expand} = useTabBarMinimized(minimizeOnScroll);\n const closeSubmenu = () => setSubmenu({anchorElement: null, menuItem: null});\n const isInside = (menuItem) => menuItem === activeItem || (menuItem.children ?? []).includes(activeItem);\n const moreHasActive = more.some(isInside);\n // la cápsula chica enseña dónde está la persona: su pestaña, «Más» si está en algo de ahí, o la primera\n const activeTab = tabs.find(isInside);\n const MinimizedIcon = (!activeTab && moreHasActive ? MoreHorizRoundedIcon : (activeTab ?? tabs[0])?.Icon) ?? MoreHorizRoundedIcon;\n // «Más» suma los conteos de lo que guarda (solo conteos: los de texto no van en la pestaña)\n const moreCount = more.reduce((total, menuItem) => {\n const badge = menuBadgeOf(menuItem, menuBadges);\n return total + (badge?.isCount ? badge.content : 0);\n }, 0);\n const moreBadge = moreCount > 0 ? {content: moreCount, isCount: true} : null;\n\n const glassSurfaceSx = floatingSurfaceOf;\n\n return (\n <>\n <Box component=\"nav\" aria-label={t('drawer.ariaLabel')}\n sx={(theme) => ({\n display: 'none',\n [PHONE_MEDIA_QUERY]: {display: 'flex'},\n position: 'fixed', zIndex: theme.zIndex.appBar, gap: 0.75, justifyContent: 'center',\n left: GLASS_PANEL_GAP_PIXELS, right: GLASS_PANEL_GAP_PIXELS,\n bottom: `calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n height: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {height: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n })}>\n <Box ref={setTabsCapsule} inert={isMinimized}\n sx={(theme) => ({...glassSurfaceSx(theme), display: 'flex', flex: 1, maxWidth: TAB_BAR_MAX_WIDTH_PIXELS, p: 0.375,\n ...appearStylesOf(theme, !isMinimized, 'left bottom')})}>\n {tabs.map((menuItem) => {\n const hasChildren = (menuItem.children ?? []).length > 0;\n return (\n <TabButton key={menuItem.to ?? menuItemLabelOf(menuItem, t)} label={menuItemLabelOf(menuItem, t)} Icon={menuItem.Icon}\n isActive={isInside(menuItem)} badge={menuBadgeOf(menuItem, menuBadges)} hasPopup={hasChildren}\n linkProps={hasChildren ? {} : {component: RouterLink, to: menuItem.to}}\n onClick={hasChildren ? (event) => setSubmenu({anchorElement: event.currentTarget, menuItem}) : undefined} />\n );\n })}\n {more.length > 0 && (\n <TabButton label={t('drawer.more')} Icon={MoreHorizRoundedIcon} isActive={moreHasActive} badge={moreBadge} hasPopup\n onClick={() => setIsMoreOpen(true)} />\n )}\n </Box>\n {/* encogida: la cápsula chica en la esquina, con la pestaña de ahorita; tocarla regresa la barra completa */}\n <ButtonBase inert={!isMinimized} onClick={expand} aria-label={t('drawer.showTabs')} aria-expanded={false}\n sx={(theme) => mergeStyles({\n ...glassSurfaceSx(theme), position: 'absolute', left: 0, bottom: 0,\n width: MINIMIZED_PIXELS.portrait, height: MINIMIZED_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {width: MINIMIZED_PIXELS.landscape, height: MINIMIZED_PIXELS.landscape},\n ...appearStylesOf(theme, isMinimized, 'left bottom'),\n }, accentTextStylesOf(theme))}>\n <MinimizedIcon sx={{fontSize: TAB_ICON_SIZE}} />\n </ButtonBase>\n {account && (\n <Box sx={(theme) => ({\n ...glassSurfaceSx(theme), flexShrink: 0, minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait, px: 0.5, display: 'flex', alignItems: 'center',\n justifyContent: 'center', [PHONE_LANDSCAPE_MEDIA_QUERY]: {minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n '& .MuiButtonBase-root': {color: 'inherit'},\n // encogida, la cuenta se queda pero del alto de la cápsula chica, pegada a su esquina\n transformOrigin: 'right bottom',\n transform: isMinimized ? `scale(${MINIMIZED_PIXELS.portrait / MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait})` : 'none',\n transition: theme.transitions.create('transform', {duration: theme.transitions.duration.short}),\n ...(isMinimized && {[PHONE_LANDSCAPE_MEDIA_QUERY]: {\n minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape,\n transform: `scale(${MINIMIZED_PIXELS.landscape / MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape})`,\n }}),\n })}>\n {account}\n </Box>\n )}\n </Box>\n\n {submenu.menuItem && (\n <SubmenuActionsMenu anchorElement={submenu.anchorElement} onClose={closeSubmenu} menuItem={submenu.menuItem} activeItem={activeItem}\n menuBadges={menuBadges} placement={SUBMENU_PLACEMENT.above} />\n )}\n\n {/* «Más»: el resto del menú en una hoja desde abajo, separada de las orillas como las hojas de iOS */}\n <Drawer anchor=\"bottom\" open={isMoreOpen} onClose={closeMore}\n slotProps={{paper: {sx: {\n m: `${GLASS_PANEL_GAP_PIXELS}px auto calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n width: `min(${MORE_SHEET_MAX_WIDTH_PIXELS}px, calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px))`,\n maxHeight: MORE_SHEET_MAX_HEIGHT, borderRadius: '20px', overflow: 'hidden',\n }}}}>\n {isEditingTabs ? (\n <TabBarEditor menuItems={menuItems} pinnedKeys={tabs.map(menuItemKeyOf)}\n onDone={(keys) => {\n setPinnedKeys(keys);\n onPinnedTabsChange?.(keys);\n leaveEditor();\n }}\n onReset={() => {\n setPinnedKeys(null);\n onPinnedTabsChange?.(null);\n leaveEditor();\n }} />\n ) : (\n <>\n <Box sx={{display: 'flex', justifyContent: 'flex-end', px: 1.5, pt: 1, flexShrink: 0}}>\n <Button size=\"small\" autoFocus={returnsFromEditor} onClick={() => setIsEditingTabs(true)}>{t('drawer.editTabs')}</Button>\n </Box>\n <SideMenu menuItems={more} activeItem={activeItem} menuBadges={menuBadges} collapsed={false} showBrand={false} footer={moreFooter}\n onNavigate={closeMore} />\n </>\n )}\n </Drawer>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MobileTabBar.js","sources":["../../src/appShell/MobileTabBar.jsx"],"sourcesContent":["// La barra de pestañas del teléfono con vidrio, como la de iOS 26: una cápsula de vidrio flotando abajo con las secciones\n// principales (mobileTabsOf: las que caben a lo ancho, y «Más» con el resto), y aparte, en su círculo de vidrio, la cuenta\n// (con todos los ajustes adentro). La reemplaza a la barra de abajo con ☰ y al cajón del menú (lo pidió el autor: «estilo\n// Apple pero con sus submenús de acciones»). También va con el minimalista cuando la barra va en cápsulas (usesMobileTabBar), sólida y\n// del gris de lo que flota (minimalFloatingStylesOf).\n//\n// - Una pestaña con submenú no navega: abre el menú de acciones de Apple arriba de ella (SubmenuActionsMenu).\n// - «Más» abre una hoja desde abajo con el resto del menú, por secciones (SideMenu, el mismo del escritorio). Arriba trae\n// «Editar la barra»: cada quien elige qué pantallas van abajo (TabBarEditor; se recuerda con usePinnedMobileTabs),\n// como pide la guía de Apple. Sin elegir, van las que marca el proyecto (mobileTab en menu.js).\n// - Cada pestaña lleva su badge como los de Apple (HIG «Tab bars»): ROJO con el número en blanco y solo para conteos (lo\n// pendiente, del servidor); un badge de texto («Nuevo») no va en la pestaña, se queda en el menú: Apple los reserva para\n// lo que pide atención.\n// - Cuántas pestañas van se mide (useFittingTabCount): las que caben con al menos MIN_TAB_PIXELS cada una; las demás van\n// a «Más». En una ventana angosta salen menos y en una ancha más, y los nombres no se cortan («Compone…»).\n// - Acostado (pantalla bajita), la barra va compacta: ícono y nombre lado a lado, para dejarle el alto al contenido.\n// - Al leer hacia abajo se encoge (como iOS 26): queda una cápsula chica con la pestaña donde está la persona, y la cuenta\n// más chica; al subir un poco, o al tocar la cápsula, regresa completa (cuándo: tabBarScroll.js). Se mueve solo con\n// opacidad y escala, lo que el navegador anima sin volver a acomodar la página: en un teléfono barato no se traba.\n// Solo se ve en el teléfono (PHONE_MEDIA_QUERY); en escritorio no ocupa nada.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useLayoutEffect, useState} from 'react';\nimport Badge from '@mui/material/Badge';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Drawer from '@mui/material/Drawer';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport MoreHorizRoundedIcon from '@mui/icons-material/MoreHorizRounded';\n\nimport {menuItemLabelOf, menuLinkPropsOf} from '@/appShell/menuItemText.js';\nimport {\n GLASS_PANEL_GAP_PIXELS, MOBILE_TAB_BAR_HEIGHT_PIXELS, PHONE_LANDSCAPE_MEDIA_QUERY, PHONE_MEDIA_QUERY,\n} from '@/appShell/shellSizes.js';\nimport SideMenu from '@/appShell/SideMenu.jsx';\nimport SubmenuActionsMenu, {SUBMENU_PLACEMENT} from '@/appShell/SubmenuActionsMenu.jsx';\nimport TabBarEditor from '@/appShell/TabBarEditor.jsx';\nimport {usePinnedMobileTabs} from '@/appShell/usePinnedMobileTabs.js';\nimport {useTabBarMinimized} from '@/appShell/useTabBarMinimized.js';\nimport {menuBadgeOf, menuItemKeyOf, mobileTabsOf} from '@/navigation/appMenu.js';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport {minimalFloatingStylesOf} from '@/theme/components/minimalComponents.js';\nimport {accentTextStylesOf, glassStylesOf, isGlassTheme} from '@/theme/glassSurface.js';\nimport {mergeStyles} from '@/theme/mergeComponents.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n// el nombre de la pestaña parado y acostado (px), lo ancho máximo de la cápsula y lo alto de la hoja de «Más»\nconst TAB_LABEL_PIXELS = Object.freeze({portrait: 10, landscape: 12});\nconst TAB_ICON_SIZE = '1.5rem';\nconst TAB_BAR_MAX_WIDTH_PIXELS = 560;\n// lo menos que mide una pestaña (px) para que su nombre se lea entero: parada, un nombre de unas 11 letras («Formularios»\n// pide 61 px, medido en Chrome); acostada, ícono y nombre lado a lado. Con menos, la pestaña se va a «Más»\nconst MIN_TAB_PIXELS = Object.freeze({portrait: 60, landscape: 104});\n// antes de medir (o si la barra no se ve): las de siempre, 4 y «Más»\nconst DEFAULT_TAB_COUNT = 5;\nconst MORE_SHEET_MAX_WIDTH_PIXELS = 640;\nconst MORE_SHEET_MAX_HEIGHT = '75vh';\nconst BADGE_MAX = 99;\n// la barra flota sobre el contenido: vidrio más opaco que el de los controles, una línea fina en la orilla y una sombra\n// debajo. Con el vidrio de siempre, en claro se perdía sobre las tarjetas blancas y no parecía flotar (lo reportaron)\nconst FLOATING_GLASS_OPACITY = 0.8;\nconst FLOATING_EDGE = Object.freeze({\n light: {outline: 'rgba(15, 23, 42, 0.12)', shadow: '0 10px 30px rgba(15, 23, 42, 0.20), 0 2px 6px rgba(15, 23, 42, 0.10)'},\n dark: {outline: 'rgba(255, 255, 255, 0.16)', shadow: '0 10px 30px rgba(0, 0, 0, 0.65), 0 2px 6px rgba(0, 0, 0, 0.4)'},\n});\n// la pestaña elegida: un tinte del acento (en claro una gota blanca sobre la cápsula blanca no se distinguía)\nconst ACTIVE_TINT_PERCENT = Object.freeze({light: 14, dark: 24});\n// encogida: lo alto de la cápsula chica (px) y a qué tamaño se va lo que desaparece\nconst MINIMIZED_PIXELS = Object.freeze({portrait: 48, landscape: 40});\nconst HIDDEN_SCALE = 0.6;\n\n/**\n * Lo que aparece o desaparece al encoger la barra: solo opacidad y escala. visibility espera a que termine la animación\n * para esconderlo, y así lo que ya no se ve tampoco se puede tocar.\n *\n * @param {object} theme\n * @param {boolean} isShown\n * @param {string} transformOrigin hacia dónde se encoge ('left bottom')\n * @returns {object}\n */\nfunction appearStylesOf(theme, isShown, transformOrigin) {\n const duration = theme.transitions.duration.short;\n return {\n transformOrigin,\n opacity: isShown ? 1 : 0,\n transform: isShown ? 'none' : `scale(${HIDDEN_SCALE})`,\n visibility: isShown ? 'visible' : 'hidden',\n transition: `${theme.transitions.create(['opacity', 'transform'], {duration})}, visibility 0s linear ${isShown ? 0 : duration}ms`,\n };\n}\n\n/**\n * La superficie de la barra y del círculo de la cuenta: vidrio más opaco, su línea fina y su sombra (la sombra va en una\n * capa detrás, ::before, para no pisar el filo de luz del vidrio, que también es una sombra). Con el minimalista, sólida.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction floatingSurfaceOf(theme) {\n // con el minimalista (barra en cápsulas) la barra es sólida: su gris de lo que flota, sin línea ni sombra\n if (!isGlassTheme(theme)) {\n return {...minimalFloatingStylesOf(theme), position: 'relative', borderRadius: '999px', height: '100%'};\n }\n return {\n ...glassStylesOf(theme, {opacity: FLOATING_GLASS_OPACITY}),\n position: 'relative',\n borderRadius: '999px',\n height: '100%',\n outline: `1px solid ${FLOATING_EDGE.light.outline}`,\n '&::before': {\n content: '\"\"', position: 'absolute', inset: 0, borderRadius: 'inherit', zIndex: -1, pointerEvents: 'none',\n boxShadow: FLOATING_EDGE.light.shadow,\n },\n ...theme.applyStyles('dark', {\n outline: `1px solid ${FLOATING_EDGE.dark.outline}`,\n '&::before': {boxShadow: FLOATING_EDGE.dark.shadow},\n }),\n };\n}\n\n/**\n * La pestaña elegida: el tinte del acento y su letra legible.\n *\n * @param {object} theme\n * @returns {object}\n */\nfunction activeTabOf(theme) {\n const primary = (theme.vars ?? theme).palette.primary.main;\n return mergeStyles({\n backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.light}%, transparent)`,\n ...theme.applyStyles('dark', {backgroundColor: `color-mix(in srgb, ${primary} ${ACTIVE_TINT_PERCENT.dark}%, transparent)`}),\n }, accentTextStylesOf(theme));\n}\n\n/**\n * Una pestaña: liga a su pantalla o, con submenú, botón que abre el menú de acciones.\n *\n * @param {object} props\n * @param {string} props.label ya traducido\n * @param {import('react').ElementType} [props.Icon]\n * @param {boolean} props.isActive\n * @param {{content: number|string, isCount: boolean}|null} props.badge\n * @param {object} [props.linkProps] {component, to} si navega\n * @param {(event: object) => void} [props.onClick]\n * @param {boolean} [props.hasPopup] abre un menú\n */\nfunction TabButton({label, Icon, isActive, badge, linkProps = {}, onClick, hasPopup = false}) {\n return (\n <ButtonBase {...linkProps} onClick={onClick} aria-haspopup={hasPopup ? 'menu' : undefined} aria-current={isActive ? 'page' : undefined}\n sx={(theme) => mergeStyles({\n flex: '1 1 0', minWidth: 0, height: '100%', borderRadius: '999px', px: 0,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 0.25,\n color: 'text.primary',\n transition: theme.transitions.create(['background-color', 'color'], {duration: theme.transitions.duration.shortest}),\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {flexDirection: 'row', gap: 0.75},\n }, isActive ? activeTabOf(theme) : {})}>\n <Badge invisible={!badge?.isCount} color=\"error\" max={BADGE_MAX} overlap=\"circular\" badgeContent={badge?.isCount ? badge.content : undefined}>\n {Icon ? <Icon sx={{fontSize: TAB_ICON_SIZE}} /> : <Box sx={{width: TAB_ICON_SIZE, height: TAB_ICON_SIZE}} />}\n </Badge>\n <Typography component=\"span\" noWrap\n sx={{...textSizeOf(TAB_LABEL_PIXELS.portrait), fontWeight: 500, maxWidth: '100%',\n [PHONE_LANDSCAPE_MEDIA_QUERY]: textSizeOf(TAB_LABEL_PIXELS.landscape)}}>\n {label}\n </Typography>\n </ButtonBase>\n );\n}\n\n/**\n * Cuántas pestañas caben en la cápsula (contando «Más»). Se mide la cápsula, que no cambia de ancho con sus pestañas (se\n * estira hasta su tope), así que la cuenta no da vueltas. Se vuelve a medir al girar el teléfono o cambiar la ventana.\n *\n * @param {HTMLElement|null} capsule\n * @param {number} minTabPixels lo menos que mide una pestaña\n * @returns {number}\n */\nfunction useFittingTabCount(capsule, minTabPixels) {\n const [tabCount, setTabCount] = useState(DEFAULT_TAB_COUNT);\n // antes de pintar: así no se ve un momento con las pestañas apretadas\n useLayoutEffect(() => {\n if (!capsule) {\n return undefined;\n }\n const measure = () => {\n const style = window.getComputedStyle(capsule);\n const innerWidth = capsule.clientWidth - Number.parseFloat(style.paddingLeft) - Number.parseFloat(style.paddingRight);\n // en escritorio la barra no se ve (ancho 0): se quedan las de siempre\n if (innerWidth > 0) {\n setTabCount(Math.max(2, Math.floor(innerWidth / minTabPixels)));\n }\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(capsule);\n return () => observer.disconnect();\n }, [capsule, minTabPixels]);\n return tabCount;\n}\n\n/**\n * @param {object} props\n * @param {object[]} props.menuItems lo que regresa composeMenu\n * @param {object|null} props.activeItem la entrada de la pantalla abierta\n * @param {Record<string, number>} [props.menuBadges] los conteos del servidor por badgeKey\n * @param {import('react').ReactNode} [props.account] la cuenta (o el engrane), en su círculo junto a la barra\n * @param {import('react').ReactNode} [props.moreFooter] lo que va al final de la hoja de «Más» (la tarjeta del menú lateral)\n * @param {boolean} [props.minimizeOnScroll] se encoge al leer hacia abajo; por omisión sí\n * @param {(keys: string[]|null) => void} [props.onPinnedTabsChange] la persona eligió sus pestañas (null: regresó a las de\n * omisión)\n */\nexport default function MobileTabBar({menuItems, activeItem, menuBadges, account, moreFooter, minimizeOnScroll = true, onPinnedTabsChange}) {\n const {t} = useToolkitTranslation('layout');\n const {Link: RouterLink} = useToolkitNavigation();\n const {pinnedKeys, setPinnedKeys} = usePinnedMobileTabs();\n const [tabsCapsule, setTabsCapsule] = useState(null);\n const isLandscape = useMediaQuery(PHONE_LANDSCAPE_MEDIA_QUERY);\n const tabCount = useFittingTabCount(tabsCapsule, isLandscape ? MIN_TAB_PIXELS.landscape : MIN_TAB_PIXELS.portrait);\n const {tabs, more} = mobileTabsOf(menuItems, pinnedKeys, tabCount);\n const [submenu, setSubmenu] = useState({anchorElement: null, menuItem: null});\n const [isMoreOpen, setIsMoreOpen] = useState(false);\n const [isEditingTabs, setIsEditingTabs] = useState(false);\n // al salir del editor su botón desaparece con el foco adentro: el foco regresa a «Editar la barra» (sin eso se perdía\n // y Escape ya no cerraba la hoja; también lo necesita quien usa lector de pantalla)\n const [returnsFromEditor, setReturnsFromEditor] = useState(false);\n const leaveEditor = () => {\n setIsEditingTabs(false);\n setReturnsFromEditor(true);\n };\n const closeMore = () => {\n setIsMoreOpen(false);\n setIsEditingTabs(false);\n setReturnsFromEditor(false);\n };\n const {isMinimized, expand} = useTabBarMinimized(minimizeOnScroll);\n const closeSubmenu = () => setSubmenu({anchorElement: null, menuItem: null});\n const isInside = (menuItem) => menuItem === activeItem || (menuItem.children ?? []).includes(activeItem);\n const moreHasActive = more.some(isInside);\n // la cápsula chica enseña dónde está la persona: su pestaña, «Más» si está en algo de ahí, o la primera\n const activeTab = tabs.find(isInside);\n const MinimizedIcon = (!activeTab && moreHasActive ? MoreHorizRoundedIcon : (activeTab ?? tabs[0])?.Icon) ?? MoreHorizRoundedIcon;\n // «Más» suma los conteos de lo que guarda (solo conteos: los de texto no van en la pestaña)\n const moreCount = more.reduce((total, menuItem) => {\n const badge = menuBadgeOf(menuItem, menuBadges);\n return total + (badge?.isCount ? badge.content : 0);\n }, 0);\n const moreBadge = moreCount > 0 ? {content: moreCount, isCount: true} : null;\n\n const glassSurfaceSx = floatingSurfaceOf;\n\n return (\n <>\n <Box component=\"nav\" aria-label={t('drawer.ariaLabel')}\n sx={(theme) => ({\n display: 'none',\n [PHONE_MEDIA_QUERY]: {display: 'flex'},\n position: 'fixed', zIndex: theme.zIndex.appBar, gap: 0.75, justifyContent: 'center',\n left: GLASS_PANEL_GAP_PIXELS, right: GLASS_PANEL_GAP_PIXELS,\n bottom: `calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n height: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {height: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n })}>\n <Box ref={setTabsCapsule} inert={isMinimized}\n sx={(theme) => ({...glassSurfaceSx(theme), display: 'flex', flex: 1, maxWidth: TAB_BAR_MAX_WIDTH_PIXELS, p: 0.375,\n ...appearStylesOf(theme, !isMinimized, 'left bottom')})}>\n {tabs.map((menuItem) => {\n const hasChildren = (menuItem.children ?? []).length > 0;\n return (\n <TabButton key={menuItem.to ?? menuItem.href ?? menuItemLabelOf(menuItem, t)} label={menuItemLabelOf(menuItem, t)} Icon={menuItem.Icon}\n isActive={isInside(menuItem)} badge={menuBadgeOf(menuItem, menuBadges)} hasPopup={hasChildren}\n linkProps={hasChildren ? {} : menuLinkPropsOf(menuItem, RouterLink)}\n onClick={hasChildren ? (event) => setSubmenu({anchorElement: event.currentTarget, menuItem}) : undefined} />\n );\n })}\n {more.length > 0 && (\n <TabButton label={t('drawer.more')} Icon={MoreHorizRoundedIcon} isActive={moreHasActive} badge={moreBadge} hasPopup\n onClick={() => setIsMoreOpen(true)} />\n )}\n </Box>\n {/* encogida: la cápsula chica en la esquina, con la pestaña de ahorita; tocarla regresa la barra completa */}\n <ButtonBase inert={!isMinimized} onClick={expand} aria-label={t('drawer.showTabs')} aria-expanded={false}\n sx={(theme) => mergeStyles({\n ...glassSurfaceSx(theme), position: 'absolute', left: 0, bottom: 0,\n width: MINIMIZED_PIXELS.portrait, height: MINIMIZED_PIXELS.portrait,\n [PHONE_LANDSCAPE_MEDIA_QUERY]: {width: MINIMIZED_PIXELS.landscape, height: MINIMIZED_PIXELS.landscape},\n ...appearStylesOf(theme, isMinimized, 'left bottom'),\n }, accentTextStylesOf(theme))}>\n <MinimizedIcon sx={{fontSize: TAB_ICON_SIZE}} />\n </ButtonBase>\n {account && (\n <Box sx={(theme) => ({\n ...glassSurfaceSx(theme), flexShrink: 0, minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait, px: 0.5, display: 'flex', alignItems: 'center',\n justifyContent: 'center', [PHONE_LANDSCAPE_MEDIA_QUERY]: {minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape},\n '& .MuiButtonBase-root': {color: 'inherit'},\n // encogida, la cuenta se queda pero del alto de la cápsula chica, pegada a su esquina\n transformOrigin: 'right bottom',\n transform: isMinimized ? `scale(${MINIMIZED_PIXELS.portrait / MOBILE_TAB_BAR_HEIGHT_PIXELS.portrait})` : 'none',\n transition: theme.transitions.create('transform', {duration: theme.transitions.duration.short}),\n ...(isMinimized && {[PHONE_LANDSCAPE_MEDIA_QUERY]: {\n minWidth: MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape,\n transform: `scale(${MINIMIZED_PIXELS.landscape / MOBILE_TAB_BAR_HEIGHT_PIXELS.landscape})`,\n }}),\n })}>\n {account}\n </Box>\n )}\n </Box>\n\n {submenu.menuItem && (\n <SubmenuActionsMenu anchorElement={submenu.anchorElement} onClose={closeSubmenu} menuItem={submenu.menuItem} activeItem={activeItem}\n menuBadges={menuBadges} placement={SUBMENU_PLACEMENT.above} />\n )}\n\n {/* «Más»: el resto del menú en una hoja desde abajo, separada de las orillas como las hojas de iOS */}\n <Drawer anchor=\"bottom\" open={isMoreOpen} onClose={closeMore}\n slotProps={{paper: {sx: {\n m: `${GLASS_PANEL_GAP_PIXELS}px auto calc(${GLASS_PANEL_GAP_PIXELS}px + env(safe-area-inset-bottom))`,\n width: `min(${MORE_SHEET_MAX_WIDTH_PIXELS}px, calc(100% - ${GLASS_PANEL_GAP_PIXELS * 2}px))`,\n maxHeight: MORE_SHEET_MAX_HEIGHT, borderRadius: '20px', overflow: 'hidden',\n }}}}>\n {isEditingTabs ? (\n <TabBarEditor menuItems={menuItems} pinnedKeys={tabs.map(menuItemKeyOf)}\n onDone={(keys) => {\n setPinnedKeys(keys);\n onPinnedTabsChange?.(keys);\n leaveEditor();\n }}\n onReset={() => {\n setPinnedKeys(null);\n onPinnedTabsChange?.(null);\n leaveEditor();\n }} />\n ) : (\n <>\n <Box sx={{display: 'flex', justifyContent: 'flex-end', px: 1.5, pt: 1, flexShrink: 0}}>\n <Button size=\"small\" autoFocus={returnsFromEditor} onClick={() => setIsEditingTabs(true)}>{t('drawer.editTabs')}</Button>\n </Box>\n <SideMenu menuItems={more} activeItem={activeItem} menuBadges={menuBadges} collapsed={false} showBrand={false} footer={moreFooter}\n onNavigate={closeMore} />\n </>\n )}\n </Drawer>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -9,10 +9,13 @@ import Tooltip from "@mui/material/Tooltip";
9
9
  import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
10
10
  import AppearanceSettings from "./AppearanceSettings.js";
11
11
  import { barMenuPlacementOf } from "./barMenuPlacement.js";
12
- function SettingsMenu({ showFontSize = true, showLanguage = true, showColorScheme = true, extraSettings }) {
12
+ import MenuLinks from "./MenuLinks.js";
13
+ function SettingsMenu({ showFontSize = true, showLanguage = true, showColorScheme = true, extraSettings, menuLinks = [] }) {
13
14
  const { t } = useToolkitTranslation("layout");
14
15
  const [anchorElement, setAnchorElement] = useState(null);
15
16
  const placement = barMenuPlacementOf(anchorElement);
17
+ const hasAppearance = showFontSize || showLanguage || showColorScheme;
18
+ const closeMenu = () => setAnchorElement(null);
16
19
  return /* @__PURE__ */ jsxs(Fragment, { children: [
17
20
  /* @__PURE__ */ jsx(Tooltip, { title: t("settings.open"), children: /* @__PURE__ */ jsx(IconButton, { size: "small", "aria-label": t("settings.open"), "aria-haspopup": "menu", onClick: (event) => setAnchorElement(event.currentTarget), children: /* @__PURE__ */ jsx(SettingsOutlinedIcon, { fontSize: "small" }) }) }),
18
21
  /* @__PURE__ */ jsxs(
@@ -20,16 +23,18 @@ function SettingsMenu({ showFontSize = true, showLanguage = true, showColorSchem
20
23
  {
21
24
  anchorEl: anchorElement,
22
25
  open: Boolean(anchorElement),
23
- onClose: () => setAnchorElement(null),
26
+ onClose: closeMenu,
24
27
  anchorOrigin: placement.anchorOrigin,
25
28
  transformOrigin: placement.transformOrigin,
26
29
  slotProps: { paper: { sx: { minWidth: 280, maxWidth: 360, py: 0.5, ...placement.gapSx } } },
27
30
  children: [
28
- /* @__PURE__ */ jsx(AppearanceSettings, { showFontSize, showLanguage, showColorScheme }),
31
+ hasAppearance && /* @__PURE__ */ jsx(AppearanceSettings, { showFontSize, showLanguage, showColorScheme }),
29
32
  extraSettings && /* @__PURE__ */ jsxs(Fragment, { children: [
30
- /* @__PURE__ */ jsx(Divider, { sx: { my: 0.5 } }),
33
+ hasAppearance && /* @__PURE__ */ jsx(Divider, { sx: { my: 0.5 } }),
31
34
  extraSettings
32
- ] })
35
+ ] }),
36
+ menuLinks.length > 0 && (hasAppearance || extraSettings) && /* @__PURE__ */ jsx(Divider, { sx: { my: 0.5 } }),
37
+ /* @__PURE__ */ jsx(MenuLinks, { links: menuLinks, onClose: closeMenu })
33
38
  ]
34
39
  }
35
40
  )
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsMenu.js","sources":["../../src/appShell/SettingsMenu.jsx"],"sourcesContent":["// Los ajustes del teléfono (del marco de walk-ot): en la barra del teléfono no caben tres botones de configuración que\n// casi no se usan, así que van juntos detrás de un engrane (AppearanceSettings: tamaño de letra, idioma y claro / oscuro).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Divider from '@mui/material/Divider';\nimport Menu from '@mui/material/Menu';\nimport Tooltip from '@mui/material/Tooltip';\nimport SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';\n\nimport AppearanceSettings from '@/appShell/AppearanceSettings.jsx';\nimport {barMenuPlacementOf} from '@/appShell/barMenuPlacement.js';\n\n/**\n * @param {object} props\n * @param {boolean} [props.showFontSize]\n * @param {boolean} [props.showLanguage]\n * @param {boolean} [props.showColorScheme]\n * @param {import('react').ReactNode} [props.extraSettings] los ajustes del proyecto, debajo de los del paquete\n */\nexport default function SettingsMenu({showFontSize = true, showLanguage = true, showColorScheme = true, extraSettings}) {\n const {t} = useToolkitTranslation('layout');\n const [anchorElement, setAnchorElement] = useState(null);\n // hacia arriba si la barra está abajo (teléfono con vidrio)\n const placement = barMenuPlacementOf(anchorElement);\n\n return (\n <>\n <Tooltip title={t('settings.open')}>\n <IconButton size=\"small\" aria-label={t('settings.open')} aria-haspopup=\"menu\" onClick={(event) => setAnchorElement(event.currentTarget)}>\n <SettingsOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={() => setAnchorElement(null)}\n anchorOrigin={placement.anchorOrigin} transformOrigin={placement.transformOrigin}\n slotProps={{paper: {sx: {minWidth: 280, maxWidth: 360, py: 0.5, ...placement.gapSx}}}}>\n <AppearanceSettings showFontSize={showFontSize} showLanguage={showLanguage} showColorScheme={showColorScheme} />\n {extraSettings && <><Divider sx={{my: 0.5}} />{extraSettings}</>}\n </Menu>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAsBA,SAAwB,aAAa,EAAC,eAAe,MAAM,eAAe,MAAM,kBAAkB,MAAM,iBAAgB;AACpH,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AAEvD,QAAM,YAAY,mBAAmB,aAAa;AAElD,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAQ,OAAO,EAAE,eAAe,GAC7B,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,EAAE,eAAe,GAAG,iBAAc,QAAO,SAAS,CAAC,UAAU,iBAAiB,MAAM,aAAa,GAClI,UAAA,oBAAC,sBAAA,EAAqB,UAAS,QAAA,CAAQ,EAAA,CAC3C,GACJ;AAAA,IACA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAK,UAAU;AAAA,QAAe,MAAM,QAAQ,aAAa;AAAA,QAAG,SAAS,MAAM,iBAAiB,IAAI;AAAA,QAC3F,cAAc,UAAU;AAAA,QAAc,iBAAiB,UAAU;AAAA,QACjE,WAAW,EAAC,OAAO,EAAC,IAAI,EAAC,UAAU,KAAK,UAAU,KAAK,IAAI,KAAK,GAAG,UAAU,MAAA,IAAM;AAAA,QACrF,UAAA;AAAA,UAAA,oBAAC,oBAAA,EAAmB,cAA4B,cAA4B,gBAAA,CAAkC;AAAA,UAC7G,iBAAiB,qBAAA,UAAA,EAAE,UAAA;AAAA,YAAA,oBAAC,SAAA,EAAQ,IAAI,EAAC,IAAI,OAAM;AAAA,YAAG;AAAA,UAAA,EAAA,CAAc;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EACjE,GACJ;AAER;"}
1
+ {"version":3,"file":"SettingsMenu.js","sources":["../../src/appShell/SettingsMenu.jsx"],"sourcesContent":["// Los ajustes sin sesión (del marco de walk-ot): tres botones de configuración que casi no se usan van juntos detrás de un\n// engrane (AppearanceSettings: tamaño de letra, idioma y claro / oscuro), con los del proyecto y sus ligas (las de la cuenta:\n// sin sesión siguen a la mano, como «Regresar al sistema»).\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Divider from '@mui/material/Divider';\nimport Menu from '@mui/material/Menu';\nimport Tooltip from '@mui/material/Tooltip';\nimport SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';\n\nimport AppearanceSettings from '@/appShell/AppearanceSettings.jsx';\nimport {barMenuPlacementOf} from '@/appShell/barMenuPlacement.js';\nimport MenuLinks from '@/appShell/MenuLinks.jsx';\n\n/**\n * @param {object} props\n * @param {boolean} [props.showFontSize]\n * @param {boolean} [props.showLanguage]\n * @param {boolean} [props.showColorScheme]\n * @param {import('react').ReactNode} [props.extraSettings] los ajustes del proyecto, debajo de los del paquete\n * @param {object[]} [props.menuLinks] las ligas propias (accountMenuItems), al final\n */\nexport default function SettingsMenu({showFontSize = true, showLanguage = true, showColorScheme = true, extraSettings, menuLinks = []}) {\n const {t} = useToolkitTranslation('layout');\n const [anchorElement, setAnchorElement] = useState(null);\n // hacia arriba si la barra está abajo (teléfono con vidrio)\n const placement = barMenuPlacementOf(anchorElement);\n const hasAppearance = showFontSize || showLanguage || showColorScheme;\n const closeMenu = () => setAnchorElement(null);\n\n return (\n <>\n <Tooltip title={t('settings.open')}>\n <IconButton size=\"small\" aria-label={t('settings.open')} aria-haspopup=\"menu\" onClick={(event) => setAnchorElement(event.currentTarget)}>\n <SettingsOutlinedIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={closeMenu}\n anchorOrigin={placement.anchorOrigin} transformOrigin={placement.transformOrigin}\n slotProps={{paper: {sx: {minWidth: 280, maxWidth: 360, py: 0.5, ...placement.gapSx}}}}>\n {hasAppearance && <AppearanceSettings showFontSize={showFontSize} showLanguage={showLanguage} showColorScheme={showColorScheme} />}\n {extraSettings && <>{hasAppearance && <Divider sx={{my: 0.5}} />}{extraSettings}</>}\n {menuLinks.length > 0 && (hasAppearance || extraSettings) && <Divider sx={{my: 0.5}} />}\n <MenuLinks links={menuLinks} onClose={closeMenu} />\n </Menu>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAyBA,SAAwB,aAAa,EAAC,eAAe,MAAM,eAAe,MAAM,kBAAkB,MAAM,eAAe,YAAY,CAAA,EAAC,GAAI;AACpI,QAAM,EAAC,EAAA,IAAK,sBAAsB,QAAQ;AAC1C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AAEvD,QAAM,YAAY,mBAAmB,aAAa;AAClD,QAAM,gBAAgB,gBAAgB,gBAAgB;AACtD,QAAM,YAAY,MAAM,iBAAiB,IAAI;AAE7C,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAQ,OAAO,EAAE,eAAe,GAC7B,UAAA,oBAAC,YAAA,EAAW,MAAK,SAAQ,cAAY,EAAE,eAAe,GAAG,iBAAc,QAAO,SAAS,CAAC,UAAU,iBAAiB,MAAM,aAAa,GAClI,UAAA,oBAAC,sBAAA,EAAqB,UAAS,QAAA,CAAQ,EAAA,CAC3C,GACJ;AAAA,IACA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAK,UAAU;AAAA,QAAe,MAAM,QAAQ,aAAa;AAAA,QAAG,SAAS;AAAA,QAChE,cAAc,UAAU;AAAA,QAAc,iBAAiB,UAAU;AAAA,QACjE,WAAW,EAAC,OAAO,EAAC,IAAI,EAAC,UAAU,KAAK,UAAU,KAAK,IAAI,KAAK,GAAG,UAAU,MAAA,IAAM;AAAA,QACpF,UAAA;AAAA,UAAA,iBAAiB,oBAAC,oBAAA,EAAmB,cAA4B,cAA4B,iBAAkC;AAAA,UAC/H,iBAAiB,qBAAA,UAAA,EAAG,UAAA;AAAA,YAAA,qCAAkB,SAAA,EAAQ,IAAI,EAAC,IAAI,OAAM;AAAA,YAAI;AAAA,UAAA,GAAc;AAAA,UAC/E,UAAU,SAAS,MAAM,iBAAiB,kBAAkB,oBAAC,SAAA,EAAQ,IAAI,EAAC,IAAI,IAAA,EAAG,CAAG;AAAA,UACrF,oBAAC,WAAA,EAAU,OAAO,WAAW,SAAS,UAAA,CAAW;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EACrD,GACJ;AAER;"}