✨ (base): Implementa Navbar con funcionalidad base
Añade `Navbar::with_position()` y `navbar::Position` al núcleo, con las clases de Basic para barras fijas y pegajosas, y `navbar::Item::form()` para mostrar un formulario dentro de la barra. Las barras sin botón de despliegue (`Simple` y `SimpleBrandLeft`) ya no colapsan: el punto de corte no les afecta y su menú se mantiene en línea, con salto de línea si no cabe. En Bootsier usan `navbar-expand` y `SimpleBrandLeft` agrupa los contenidos junto a la marca.
This commit is contained in:
parent
f8f023b513
commit
c47724bb68
14 changed files with 1298 additions and 209 deletions
|
|
@ -1,7 +1,7 @@
|
|||
//! Definiciones para crear barras de navegación ([`Navbar`]) y sus elementos ([`Item`]).
|
||||
|
||||
mod props;
|
||||
pub use props::Layout;
|
||||
pub use props::{Layout, Position};
|
||||
|
||||
mod component;
|
||||
pub use component::Navbar;
|
||||
|
|
|
|||
|
|
@ -88,6 +88,9 @@ pub struct Navbar {
|
|||
#[default(Breakpoint::Md)]
|
||||
#[getters(copy)]
|
||||
expand: Breakpoint,
|
||||
/// Devuelve la posición de la barra en el documento.
|
||||
#[getters(copy)]
|
||||
position: navbar::Position,
|
||||
/// Devuelve la lista de contenidos.
|
||||
items: Children,
|
||||
}
|
||||
|
|
@ -115,6 +118,11 @@ impl Component for Navbar {
|
|||
};
|
||||
self.alter_prop(PropsOp::prepend_classes(class));
|
||||
self.alter_prop(PropsOp::prepend_classes("navbar"));
|
||||
|
||||
let position = self.position().as_str();
|
||||
if !position.is_empty() {
|
||||
self.alter_prop(PropsOp::add_classes(position));
|
||||
}
|
||||
}
|
||||
|
||||
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
|
|
@ -237,11 +245,23 @@ impl Navbar {
|
|||
/// Por debajo de ese punto de corte, en las disposiciones con botón de despliegue el contenido
|
||||
/// queda oculto tras el botón. Con [`Breakpoint::Xs`] la barra nunca colapsa. Por defecto es
|
||||
/// [`Breakpoint::Md`].
|
||||
///
|
||||
/// Sólo tiene efecto en las disposiciones con botón de despliegue: en
|
||||
/// [`navbar::Layout::Simple`] y [`navbar::Layout::SimpleBrandLeft`] no hay nada que colapsar y
|
||||
/// la barra siempre se muestra en línea.
|
||||
pub fn with_expand(mut self, bp: Breakpoint) -> Self {
|
||||
self.expand = bp;
|
||||
self
|
||||
}
|
||||
|
||||
/// Define dónde se mostrará la barra de navegación dentro del documento.
|
||||
///
|
||||
/// Por defecto es [`navbar::Position::Static`], la barra fluye con el documento.
|
||||
pub fn with_position(mut self, position: navbar::Position) -> Self {
|
||||
self.position = position;
|
||||
self
|
||||
}
|
||||
|
||||
/// Añade un nuevo contenido a la barra de navegación o modifica la lista de contenidos de la
|
||||
/// barra con una operación [`TypedOp`].
|
||||
///
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ pub enum Item {
|
|||
Brand(Embed<Brand>),
|
||||
/// Representa un menú de navegación [`Nav`].
|
||||
Nav(Embed<Nav>),
|
||||
/// Representa un formulario [`Form`] (búsqueda, botones...), renderizado tal cual.
|
||||
Form(Embed<Form>),
|
||||
/// Representa un *texto localizado* libre.
|
||||
Text(Lc),
|
||||
}
|
||||
|
|
@ -33,6 +35,7 @@ impl Component for Item {
|
|||
Self::Void => None,
|
||||
Self::Brand(brand) => brand.id(),
|
||||
Self::Nav(nav) => nav.id(),
|
||||
Self::Form(form) => form.id(),
|
||||
Self::Text(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +56,7 @@ impl Component for Item {
|
|||
Self::Void => html! {},
|
||||
Self::Brand(brand) => html! { (brand.render(cx).await) },
|
||||
Self::Nav(nav) => html! { (nav.render(cx).await) },
|
||||
Self::Form(form) => html! { (form.render(cx).await) },
|
||||
Self::Text(text) => html! {
|
||||
span class="navbar-text" {
|
||||
(text.using(cx))
|
||||
|
|
@ -77,6 +81,27 @@ impl Item {
|
|||
Self::Nav(Embed::with(item))
|
||||
}
|
||||
|
||||
/// Crea un elemento de tipo [`Form`] para añadir al contenido de [`Navbar`](super::Navbar).
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// Formulario de búsqueda dentro de la barra:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use pagetop::prelude::*;
|
||||
/// let search = Form::new().with_action("/search").with_child(
|
||||
/// Flex::new()
|
||||
/// .with_gap(align::Gap::Both(UnitValue::RelRem(0.5)))
|
||||
/// .with_child(form::input::Field::search().with_name("q"))
|
||||
/// .with_child(Button::submit(Lc::n("Search"))),
|
||||
/// );
|
||||
///
|
||||
/// let navbar = Navbar::simple().with_item(navbar::Item::form(search));
|
||||
/// ```
|
||||
pub fn form(form: Form) -> Self {
|
||||
Self::Form(Embed::with(form))
|
||||
}
|
||||
|
||||
/// Crea un elemento con un *texto localizado*, mostrado sin interacción.
|
||||
pub fn text(item: Lc) -> Self {
|
||||
Self::Text(item)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use crate::prelude::*;
|
|||
/// [`Navbar`](super::Navbar).
|
||||
///
|
||||
/// Sólo recoge las combinaciones de marca y botón de despliegue independientes de cualquier
|
||||
/// framework CSS. Un tema puede definir su propia variante de disposición (posiciones fijas,
|
||||
/// contenido en un panel lateral...) con su propio tipo, sin depender de éste.
|
||||
/// framework CSS. Un tema puede definir su propia variante de disposición (contenido en un panel
|
||||
/// lateral...) con su propio tipo, sin depender de éste.
|
||||
#[derive(AutoDefault, Clone, Debug)]
|
||||
pub enum Layout {
|
||||
/// Barra simple, sin marca de identidad y sin botón de despliegue.
|
||||
|
|
@ -30,3 +30,39 @@ pub enum Layout {
|
|||
/// Barra con botón de despliegue y marca de identidad, en ese orden.
|
||||
BrandRight(Embed<Brand>),
|
||||
}
|
||||
|
||||
// **< Position >***********************************************************************************
|
||||
|
||||
/// Posición de una barra de navegación [`Navbar`](super::Navbar) en el documento.
|
||||
///
|
||||
/// Las variantes `Fixed*` sacan la barra del flujo del documento, por lo que pueden tapar el
|
||||
/// contenido que queda debajo: hay que reservarle espacio.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Position {
|
||||
/// Barra normal, fluye con el documento.
|
||||
#[default]
|
||||
Static,
|
||||
/// Barra fijada en la parte superior, siempre visible.
|
||||
FixedTop,
|
||||
/// Barra fijada en la parte inferior, siempre visible.
|
||||
FixedBottom,
|
||||
/// Barra que se fija en la parte superior al hacer *scroll*.
|
||||
StickyTop,
|
||||
/// Barra que se fija en la parte inferior al hacer *scroll*.
|
||||
StickyBottom,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
/// Devuelve la clase CSS asociada a la posición, o una cadena vacía si es `Static`.
|
||||
#[rustfmt::skip]
|
||||
#[inline]
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Static => "",
|
||||
Self::FixedTop => "fixed-top",
|
||||
Self::FixedBottom => "fixed-bottom",
|
||||
Self::StickyTop => "sticky-top",
|
||||
Self::StickyBottom => "sticky-bottom",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue