diff --git a/assets/css/basic.css b/assets/css/basic.css index b323e0e0..92e1b943 100644 --- a/assets/css/basic.css +++ b/assets/css/basic.css @@ -425,6 +425,11 @@ body { .dropdown-menu.show { display: block; } +/* Anchors the menu's right edge to the button's right edge, so it opens inwards. */ +.dropdown-menu-end { + left: auto; + right: 0; +} /* Options and headers stay on one line, so the menu grows to fit the longest one. */ .dropdown-item { diff --git a/extensions/pagetop-admin/src/lib.rs b/extensions/pagetop-admin/src/lib.rs index 7e3d3e35..fbf6b053 100644 --- a/extensions/pagetop-admin/src/lib.rs +++ b/extensions/pagetop-admin/src/lib.rs @@ -100,8 +100,9 @@ use pagetop_seaorm::install_migrations; include_locales!(LOCALES_ADMIN); /// Ruta raíz del panel de administración. No es configurable: otras piezas del ecosistema (p. ej. -/// las rutas de `pagetop-user`, `/admin/user/...`) ya asumen este valor de forma literal. -pub(crate) const ADMIN_BASE_PATH: &str = "/admin"; +/// las rutas de `pagetop-user`, `/admin/user/...`) ya asumen este valor de forma literal. Es +/// pública para que otras extensiones enlacen al panel sin repetir la ruta. +pub const ADMIN_BASE_PATH: &str = "/admin"; pub mod action; pub mod component; diff --git a/extensions/pagetop-admin/src/registry.rs b/extensions/pagetop-admin/src/registry.rs index 5bc315fd..3adcfd50 100644 --- a/extensions/pagetop-admin/src/registry.rs +++ b/extensions/pagetop-admin/src/registry.rs @@ -269,6 +269,22 @@ pub fn global() -> &'static AdminRegistry { REGISTRY.get().expect("AdminRegistry not initialized") } +/// Devuelve `true` si el usuario de la petición actual tiene acceso a alguna página del panel, es +/// decir, si `/admin` no le devolvería un acceso denegado. +/// +/// Permite a otras extensiones (p. ej. el menú de cuenta de `pagetop-user`) ofrecer el enlace a +/// la administración sólo cuando procede. +pub fn can_access_admin(cx: &Context) -> bool { + let reg = global(); + reg.ordered_sections().into_iter().any(|section| { + section.is_visible(cx) + && reg + .pages_for_section(§ion.key) + .into_iter() + .any(|page| page.is_accessible(cx)) + }) +} + // **< Menú de administración >********************************************************************* /// Construye la barra de navegación de administración para el usuario de la petición actual: un @@ -308,7 +324,5 @@ pub fn admin_navbar(cx: &Context) -> Navbar { nav = nav.with_item(nav::Item::dropdown(menu)); } - Navbar::simple() - .with_position(navbar::Position::StickyTop) - .with_item(navbar::Item::nav(nav)) + Navbar::simple().with_item(navbar::Item::nav(nav)) } diff --git a/extensions/pagetop-bootsier/src/theme/bs/dropdown.rs b/extensions/pagetop-bootsier/src/theme/bs/dropdown.rs index 829f0f24..f988605f 100644 --- a/extensions/pagetop-bootsier/src/theme/bs/dropdown.rs +++ b/extensions/pagetop-bootsier/src/theme/bs/dropdown.rs @@ -56,6 +56,11 @@ pub trait DropdownBootsier { fn with_direction(self, direction: Direction) -> Self; /// Configura la alineación horizontal (con posible comportamiento *responsive* adicional). + /// + /// Si se deja en [`MenuAlign::Start`] (por defecto), se respeta [`with_menu_end()`]; cualquier + /// otra alineación prevalece sobre él. + /// + /// [`with_menu_end()`]: pagetop::base::component::Dropdown::with_menu_end fn with_menu_align(self, align: MenuAlign) -> Self; /// Configura la posición del menú. @@ -135,9 +140,15 @@ pub(crate) async fn render( let direction = dropdown .props() .extra_or(EXTRA_DIRECTION, Direction::default()); - let menu_align = dropdown + let menu_align = match dropdown .props() - .extra_or(EXTRA_MENU_ALIGN, MenuAlign::default()); + .extra_or(EXTRA_MENU_ALIGN, MenuAlign::default()) + { + // `with_menu_end()` del núcleo se traduce a `MenuAlign::End`, pero sólo si no hay una + // alineación propia diferente a la predeterminada (`Start`), que prevalece. + MenuAlign::Start if *dropdown.menu_end() => MenuAlign::End, + align => align, + }; let menu_position = dropdown .props() .extra_or(EXTRA_MENU_POSITION, MenuPosition::default()); diff --git a/extensions/pagetop-bootsier/src/theme/bs/nav.rs b/extensions/pagetop-bootsier/src/theme/bs/nav.rs index 5567bef9..90373526 100644 --- a/extensions/pagetop-bootsier/src/theme/bs/nav.rs +++ b/extensions/pagetop-bootsier/src/theme/bs/nav.rs @@ -169,6 +169,11 @@ pub(crate) async fn item_render(item: &Item, cx: &mut Context) -> Result Result String { + let mut dropdown = dropdown + .with_title(Lc::n("Menu")) + .with_item(bs::dropdown::Item::link(Lc::n("Home"), "/")); + dropdown.render(&mut cx()).await.into_string() +} + +#[pagetop::test] +async fn dropdown_menu_end_maps_to_the_end_alignment() { + let html = dropdown_menu_html(bs::Dropdown::new().with_menu_end(true)).await; + assert!(html.contains(r#"