(pagetop): Mejora API y doc. de Children

- `From<T: Component> for ChildOp: with_child()` acepta componentes
  directamente sin envolverlos en `Child::with(...)`.
- `From<Child> for ChildOp` para completar las conversiones implícitas.
- Actualiza ejemplos y tests con la nueva API en bootsier y aliner.
This commit is contained in:
Manuel Cillero 2026-03-29 11:54:20 +02:00
parent cd9454a729
commit d10d546418
27 changed files with 346 additions and 313 deletions

View file

@ -69,10 +69,10 @@ use pagetop_aliner::Aliner;
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_theme(&Aliner)
.add_child(
.with_child(
Block::new()
.with_title(L10n::l("sample_title"))
.add_child(Html::with(|cx| html! {
.with_child(Html::with(|cx| html! {
p { (L10n::l("sample_content").using(cx)) }
})),
)
@ -122,7 +122,7 @@ impl Theme for Aliner {
))
.alter_child_in(
&DefaultRegion::Footer,
ChildOp::AddIfEmpty(Child::with(PoweredBy::new())),
ChildOp::AddIfEmpty(PoweredBy::new().into()),
);
}
}

View file

@ -150,17 +150,11 @@ impl Container {
self
}
/// Añade un nuevo componente hijo al contenedor.
#[inline]
pub fn add_child(mut self, component: impl Component) -> Self {
self.children.add(Child::with(component));
self
}
/// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`].
/// Añade un nuevo componente al contenedor o modifica la lista de componentes (`children`) con
/// una operación [`ChildOp`].
#[builder_fn]
pub fn with_child(mut self, op: ChildOp) -> Self {
self.children.alter_child(op);
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
self.children.alter_child(op.into());
self
}
}

View file

@ -17,11 +17,11 @@
//! .with_button_color(ButtonColor::Background(Color::Secondary))
//! .with_auto_close(dropdown::AutoClose::ClickableInside)
//! .with_direction(dropdown::Direction::Dropend)
//! .add_item(dropdown::Item::link(L10n::n("Home"), |_| "/".into()))
//! .add_item(dropdown::Item::link_blank(L10n::n("External"), |_| "https://google.es".into()))
//! .add_item(dropdown::Item::divider())
//! .add_item(dropdown::Item::header(L10n::n("User session")))
//! .add_item(dropdown::Item::button(L10n::n("Sign out")));
//! .with_item(dropdown::Item::link(L10n::n("Home"), |_| "/".into()))
//! .with_item(dropdown::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
//! .with_item(dropdown::Item::divider())
//! .with_item(dropdown::Item::header(L10n::n("User session")))
//! .with_item(dropdown::Item::button(L10n::n("Sign out")));
//! ```
mod props;

View file

@ -240,27 +240,22 @@ impl Dropdown {
self
}
/// Añade un nuevo elemento hijo al menú.
#[inline]
pub fn add_item(mut self, item: dropdown::Item) -> Self {
self.items.add(Child::with(item));
self
}
/// Modifica la lista de elementos del menú aplicando una operación [`ChildOp`].
/// Añade un nuevo elemento al menú o modifica la lista de elementos del menú con una operación
/// [`ChildOp`].
///
/// Para añadir elementos usa [`Child::with(item)`](Child::with):
/// # Ejemplo
///
/// ```rust,ignore
/// dropdown.with_items(ChildOp::Add(Child::with(dropdown::Item::link(...))));
/// dropdown.with_items(ChildOp::AddMany(vec![
/// Child::with(dropdown::Item::link(...)),
/// Child::with(dropdown::Item::divider()),
/// dropdown.with_item(dropdown::Item::link("Opción", "/ruta"));
/// dropdown.with_item(ChildOp::AddMany(vec![
/// dropdown::Item::link(...).into(),
/// dropdown::Item::divider().into(),
/// dropdown::Item::link(...).into(),
/// ]));
/// ```
#[builder_fn]
pub fn with_items(mut self, op: ChildOp) -> Self {
self.items.alter_child(op);
pub fn with_item(mut self, op: impl Into<ChildOp>) -> Self {
self.items.alter_child(op.into());
self
}
}

View file

@ -25,7 +25,7 @@ use crate::theme::form;
/// .with_action("/search")
/// .with_method(form::Method::Get)
/// .with_classes(ClassesOp::Add, "mb-3")
/// .add_child(Input::new().with_name("q"));
/// .with_child(Input::new().with_name("q"));
/// ```
#[derive(AutoDefault, Clone, Debug, Getters)]
pub struct Form {
@ -114,17 +114,11 @@ impl Form {
self
}
/// Añade un nuevo componente hijo al formulario.
#[inline]
pub fn add_child(mut self, component: impl Component) -> Self {
self.children.add(Child::with(component));
self
}
/// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`].
/// Añade un nuevo componente al formulario o modifica la lista de de componentes (`children`)
/// con una operación [`ChildOp`].
#[builder_fn]
pub fn with_child(mut self, op: ChildOp) -> Self {
self.children.alter_child(op);
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
self.children.alter_child(op.into());
self
}
}

View file

@ -65,17 +65,11 @@ impl Fieldset {
self
}
/// Añade un nuevo componente hijo al `fieldset`.
#[inline]
pub fn add_child(mut self, component: impl Component) -> Self {
self.children.add(Child::with(component));
self
}
/// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`].
/// Añade un nuevo componente al `fieldset` o modifica la lista de de componentes (`children`)
/// con una operación [`ChildOp`].
#[builder_fn]
pub fn with_child(mut self, op: ChildOp) -> Self {
self.children.alter_child(op);
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
self.children.alter_child(op.into());
self
}
}

View file

@ -14,17 +14,17 @@
//! # use pagetop_bootsier::prelude::*;
//! let nav = Nav::tabs()
//! .with_layout(nav::Layout::End)
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .add_item(nav::Item::link_blank(L10n::n("External"), |_| "https://google.es".into()))
//! .add_item(nav::Item::dropdown(
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .with_item(nav::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
//! .with_item(nav::Item::dropdown(
//! Dropdown::new()
//! .with_title(L10n::n("Options"))
//! .with_items(ChildOp::AddMany(vec![
//! Child::with(dropdown::Item::link(L10n::n("Action"), |_| "/action".into())),
//! Child::with(dropdown::Item::link(L10n::n("Another"), |_| "/another".into())),
//! .with_item(ChildOp::AddMany(vec![
//! dropdown::Item::link(L10n::n("Action"), |_| "/action".into()).into(),
//! dropdown::Item::link(L10n::n("Another"), |_| "/another".into()).into(),
//! ])),
//! ))
//! .add_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()));
//! .with_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()));
//! ```
mod props;

View file

@ -102,28 +102,21 @@ impl Nav {
self
}
/// Añade un nuevo elemento hijo al menú.
pub fn add_item(mut self, item: nav::Item) -> Self {
self.items.add(Child::with(item));
self
}
/// Modifica la lista de elementos del menú aplicando una operación [`ChildOp`].
/// Añade un nuevo elemento al menú o modifica la lista de elementos del menú con una operación
/// [`ChildOp`].
///
/// Para añadir elementos usa [`Child::with(item)`](Child::with):
/// # Ejemplo
///
/// ```rust,ignore
/// nav.with_items(ChildOp::Add(Child::with(nav::Item::link(...))));
/// nav.with_items(ChildOp::AddMany(vec![
/// Child::with(nav::Item::link(...)),
/// Child::with(nav::Item::link_disabled(...)),
/// nav.with_item(nav::Item::link("Inicio", "/"));
/// nav.with_item(ChildOp::AddMany(vec![
/// nav::Item::link(...).into(),
/// nav::Item::link_disabled(...).into(),
/// ]));
/// ```
///
/// Para la mayoría de los casos, [`add_item()`](Self::add_item) es más directo.
#[builder_fn]
pub fn with_items(mut self, op: ChildOp) -> Self {
self.items.alter_child(op);
pub fn with_item(mut self, op: impl Into<ChildOp>) -> Self {
self.items.alter_child(op.into());
self
}
}

View file

@ -15,11 +15,11 @@
//! # use pagetop::prelude::*;
//! # use pagetop_bootsier::prelude::*;
//! let navbar = Navbar::simple()
//! .add_item(navbar::Item::nav(
//! .with_item(navbar::Item::nav(
//! Nav::new()
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .add_item(nav::Item::link(L10n::n("About"), |_| "/about".into()))
//! .add_item(nav::Item::link(L10n::n("Contact"), |_| "/contact".into()))
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .with_item(nav::Item::link(L10n::n("About"), |_| "/about".into()))
//! .with_item(nav::Item::link(L10n::n("Contact"), |_| "/contact".into()))
//! ));
//! ```
//!
@ -30,11 +30,11 @@
//! # use pagetop_bootsier::prelude::*;
//! let navbar = Navbar::simple_toggle()
//! .with_expand(BreakPoint::MD)
//! .add_item(navbar::Item::nav(
//! .with_item(navbar::Item::nav(
//! Nav::new()
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .add_item(nav::Item::link_blank(L10n::n("Docs"), |_| "https://sample.com".into()))
//! .add_item(nav::Item::link(L10n::n("Support"), |_| "/support".into()))
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .with_item(nav::Item::link_blank(L10n::n("Docs"), |_| "https://docs.rs".into()))
//! .with_item(nav::Item::link(L10n::n("Support"), |_| "/support".into()))
//! ));
//! ```
//!
@ -48,20 +48,20 @@
//! .with_route(Some(|cx| cx.route("/")));
//!
//! let navbar = Navbar::brand_left(brand)
//! .add_item(navbar::Item::nav(
//! .with_item(navbar::Item::nav(
//! Nav::new()
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .add_item(nav::Item::dropdown(
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .with_item(nav::Item::dropdown(
//! Dropdown::new()
//! .with_title(L10n::n("Tools"))
//! .add_item(dropdown::Item::link(
//! .with_item(dropdown::Item::link(
//! L10n::n("Generator"), |_| "/tools/gen".into())
//! )
//! .add_item(dropdown::Item::link(
//! .with_item(dropdown::Item::link(
//! L10n::n("Reports"), |_| "/tools/reports".into())
//! )
//! ))
//! .add_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()))
//! .with_item(nav::Item::link_disabled(L10n::n("Disabled"), |_| "#".into()))
//! ));
//! ```
//!
@ -76,10 +76,10 @@
//!
//! let navbar = Navbar::brand_right(brand)
//! .with_expand(BreakPoint::LG)
//! .add_item(navbar::Item::nav(
//! .with_item(navbar::Item::nav(
//! Nav::pills()
//! .add_item(nav::Item::link(L10n::n("Dashboard"), |_| "/dashboard".into()))
//! .add_item(nav::Item::link(L10n::n("Users"), |_| "/users".into()))
//! .with_item(nav::Item::link(L10n::n("Dashboard"), |_| "/dashboard".into()))
//! .with_item(nav::Item::link(L10n::n("Users"), |_| "/users".into()))
//! ));
//! ```
//!
@ -95,15 +95,15 @@
//! .with_backdrop(offcanvas::Backdrop::Enabled);
//!
//! let navbar = Navbar::offcanvas(oc)
//! .add_item(navbar::Item::nav(
//! .with_item(navbar::Item::nav(
//! Nav::new()
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .add_item(nav::Item::link(L10n::n("Profile"), |_| "/profile".into()))
//! .add_item(nav::Item::dropdown(
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
//! .with_item(nav::Item::link(L10n::n("Profile"), |_| "/profile".into()))
//! .with_item(nav::Item::dropdown(
//! Dropdown::new()
//! .with_title(L10n::n("More"))
//! .add_item(dropdown::Item::link(L10n::n("Settings"), |_| "/settings".into()))
//! .add_item(dropdown::Item::link(L10n::n("Help"), |_| "/help".into()))
//! .with_item(dropdown::Item::link(L10n::n("Settings"), |_| "/settings".into()))
//! .with_item(dropdown::Item::link(L10n::n("Help"), |_| "/help".into()))
//! ))
//! ));
//! ```
@ -119,11 +119,11 @@
//!
//! let navbar = Navbar::brand_left(brand)
//! .with_position(navbar::Position::FixedTop)
//! .add_item(navbar::Item::nav(
//! .with_item(navbar::Item::nav(
//! Nav::new()
//! .add_item(nav::Item::link(L10n::n("Dashboard"), |_| "/".into()))
//! .add_item(nav::Item::link(L10n::n("Donors"), |_| "/donors".into()))
//! .add_item(nav::Item::link(L10n::n("Stock"), |_| "/stock".into()))
//! .with_item(nav::Item::link(L10n::n("Dashboard"), |_| "/".into()))
//! .with_item(nav::Item::link(L10n::n("Donors"), |_| "/donors".into()))
//! .with_item(nav::Item::link(L10n::n("Stock"), |_| "/stock".into()))
//! ));
//! ```

View file

@ -255,29 +255,21 @@ impl Navbar {
self
}
/// Añade un nuevo contenido hijo.
#[inline]
pub fn add_item(mut self, item: navbar::Item) -> Self {
self.items.add(Child::with(item));
self
}
/// Modifica la lista de contenidos de la barra aplicando una operación [`ChildOp`].
/// Añade un nuevo contenido a la barra de navegación o modifica la lista de contenidos de la
/// barra con una operación [`ChildOp`].
///
/// Para añadir elementos usa [`Child::with(item)`](Child::with):
/// # Ejemplo
///
/// ```rust,ignore
/// navbar.with_items(ChildOp::Add(Child::with(navbar::Item::nav(...))));
/// navbar.with_items(ChildOp::AddMany(vec![
/// Child::with(navbar::Item::nav(...)),
/// Child::with(navbar::Item::text(...)),
/// navbar.with_item(navbar::Item::nav(...));
/// navbar.with_item(ChildOp::AddMany(vec![
/// navbar::Item::nav(...).into(),
/// navbar::Item::text(...).into(),
/// ]));
/// ```
///
/// Para la mayoría de los casos, [`add_item()`](Self::add_item) es más directo.
#[builder_fn]
pub fn with_items(mut self, op: ChildOp) -> Self {
self.items.alter_child(op);
pub fn with_item(mut self, op: impl Into<ChildOp>) -> Self {
self.items.alter_child(op.into());
self
}
}

View file

@ -12,11 +12,11 @@
//! .with_backdrop(offcanvas::Backdrop::Enabled)
//! .with_body_scroll(offcanvas::BodyScroll::Enabled)
//! .with_visibility(offcanvas::Visibility::Default)
//! .add_child(Dropdown::new()
//! .with_child(Dropdown::new()
//! .with_title(L10n::n("Menu"))
//! .add_item(dropdown::Item::label(L10n::n("Label")))
//! .add_item(dropdown::Item::link_blank(L10n::n("Google"), |_| "https://google.es".into()))
//! .add_item(dropdown::Item::link(L10n::n("Sign out"), |_| "/signout".into()))
//! .with_item(dropdown::Item::label(L10n::n("Label")))
//! .with_item(dropdown::Item::link_blank(L10n::n("Docs"), |_| "https://docs.rs".into()))
//! .with_item(dropdown::Item::link(L10n::n("Sign out"), |_| "/signout".into()))
//! );
//! ```

View file

@ -135,17 +135,11 @@ impl Offcanvas {
self
}
/// Añade un nuevo componente hijo al panel.
#[inline]
pub fn add_child(mut self, child: impl Component) -> Self {
self.children.add(Child::with(child));
self
}
/// Modifica la lista de componentes (`children`) aplicando una operación [`ChildOp`].
/// Añade un nuevo componente al panel o modifica la lista de componentes (`children`) con una
/// operación [`ChildOp`].
#[builder_fn]
pub fn with_children(mut self, op: ChildOp) -> Self {
self.children.alter_child(op);
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
self.children.alter_child(op.into());
self
}