✨ (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:
parent
cd9454a729
commit
d10d546418
27 changed files with 346 additions and 313 deletions
|
|
@ -14,7 +14,7 @@ async fn hello_name(
|
||||||
) -> ResultPage<Markup, ErrorPage> {
|
) -> ResultPage<Markup, ErrorPage> {
|
||||||
let name = path.into_inner();
|
let name = path.into_inner();
|
||||||
Page::new(request)
|
Page::new(request)
|
||||||
.add_child(Html::with(move |_| {
|
.with_child(Html::with(move |_| {
|
||||||
html! {
|
html! {
|
||||||
h1 style="text-align: center;" { "Hello " (name) "!" }
|
h1 style="text-align: center;" { "Hello " (name) "!" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ impl Extension for HelloWorld {
|
||||||
|
|
||||||
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||||
Page::new(request)
|
Page::new(request)
|
||||||
.add_child(Html::with(|_| {
|
.with_child(Html::with(|_| {
|
||||||
html! {
|
html! {
|
||||||
h1 style="text-align: center;" { "Hello World!" }
|
h1 style="text-align: center;" { "Hello World!" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,79 +12,79 @@ impl Extension for SuperMenu {
|
||||||
fn initialize(&self) {
|
fn initialize(&self) {
|
||||||
let navbar_menu = Navbar::brand_left(navbar::Brand::new())
|
let navbar_menu = Navbar::brand_left(navbar::Brand::new())
|
||||||
.with_expand(BreakPoint::LG)
|
.with_expand(BreakPoint::LG)
|
||||||
.add_item(navbar::Item::nav(
|
.with_item(navbar::Item::nav(
|
||||||
Nav::new()
|
Nav::new()
|
||||||
.add_item(nav::Item::link(L10n::l("sample_menus_item_link"), |cx| {
|
.with_item(nav::Item::link(L10n::l("sample_menus_item_link"), |cx| {
|
||||||
cx.route("/")
|
cx.route("/")
|
||||||
}))
|
}))
|
||||||
.add_item(nav::Item::link_blank(
|
.with_item(nav::Item::link_blank(
|
||||||
L10n::l("sample_menus_item_blank"),
|
L10n::l("sample_menus_item_blank"),
|
||||||
|_| "https://docs.rs/pagetop".into(),
|
|_| "https://docs.rs/pagetop".into(),
|
||||||
))
|
))
|
||||||
.add_item(nav::Item::dropdown(
|
.with_item(nav::Item::dropdown(
|
||||||
Dropdown::new()
|
Dropdown::new()
|
||||||
.with_title(L10n::l("sample_menus_test_title"))
|
.with_title(L10n::l("sample_menus_test_title"))
|
||||||
.add_item(dropdown::Item::header(L10n::l("sample_menus_dev_header")))
|
.with_item(dropdown::Item::header(L10n::l("sample_menus_dev_header")))
|
||||||
.add_item(dropdown::Item::link(
|
.with_item(dropdown::Item::link(
|
||||||
L10n::l("sample_menus_dev_getting_started"),
|
L10n::l("sample_menus_dev_getting_started"),
|
||||||
|cx| cx.route("/dev/getting-started"),
|
|cx| cx.route("/dev/getting-started"),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::link(
|
.with_item(dropdown::Item::link(
|
||||||
L10n::l("sample_menus_dev_guides"),
|
L10n::l("sample_menus_dev_guides"),
|
||||||
|cx| cx.route("/dev/guides"),
|
|cx| cx.route("/dev/guides"),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::link_blank(
|
.with_item(dropdown::Item::link_blank(
|
||||||
L10n::l("sample_menus_dev_forum"),
|
L10n::l("sample_menus_dev_forum"),
|
||||||
|_| "https://forum.example.dev".into(),
|
|_| "https://forum.example.dev".into(),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::divider())
|
.with_item(dropdown::Item::divider())
|
||||||
.add_item(dropdown::Item::header(L10n::l("sample_menus_sdk_header")))
|
.with_item(dropdown::Item::header(L10n::l("sample_menus_sdk_header")))
|
||||||
.add_item(dropdown::Item::link(
|
.with_item(dropdown::Item::link(
|
||||||
L10n::l("sample_menus_sdk_rust"),
|
L10n::l("sample_menus_sdk_rust"),
|
||||||
|cx| cx.route("/dev/sdks/rust"),
|
|cx| cx.route("/dev/sdks/rust"),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::link(L10n::l("sample_menus_sdk_js"), |cx| {
|
.with_item(dropdown::Item::link(L10n::l("sample_menus_sdk_js"), |cx| {
|
||||||
cx.route("/dev/sdks/js")
|
cx.route("/dev/sdks/js")
|
||||||
}))
|
}))
|
||||||
.add_item(dropdown::Item::link(
|
.with_item(dropdown::Item::link(
|
||||||
L10n::l("sample_menus_sdk_python"),
|
L10n::l("sample_menus_sdk_python"),
|
||||||
|cx| cx.route("/dev/sdks/python"),
|
|cx| cx.route("/dev/sdks/python"),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::divider())
|
.with_item(dropdown::Item::divider())
|
||||||
.add_item(dropdown::Item::header(L10n::l(
|
.with_item(dropdown::Item::header(L10n::l(
|
||||||
"sample_menus_plugin_header",
|
"sample_menus_plugin_header",
|
||||||
)))
|
)))
|
||||||
.add_item(dropdown::Item::link(
|
.with_item(dropdown::Item::link(
|
||||||
L10n::l("sample_menus_plugin_auth"),
|
L10n::l("sample_menus_plugin_auth"),
|
||||||
|cx| cx.route("/dev/sdks/rust/plugins/auth"),
|
|cx| cx.route("/dev/sdks/rust/plugins/auth"),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::link(
|
.with_item(dropdown::Item::link(
|
||||||
L10n::l("sample_menus_plugin_cache"),
|
L10n::l("sample_menus_plugin_cache"),
|
||||||
|cx| cx.route("/dev/sdks/rust/plugins/cache"),
|
|cx| cx.route("/dev/sdks/rust/plugins/cache"),
|
||||||
))
|
))
|
||||||
.add_item(dropdown::Item::divider())
|
.with_item(dropdown::Item::divider())
|
||||||
.add_item(dropdown::Item::label(L10n::l("sample_menus_item_label")))
|
.with_item(dropdown::Item::label(L10n::l("sample_menus_item_label")))
|
||||||
.add_item(dropdown::Item::link_disabled(
|
.with_item(dropdown::Item::link_disabled(
|
||||||
L10n::l("sample_menus_item_disabled"),
|
L10n::l("sample_menus_item_disabled"),
|
||||||
|cx| cx.route("#"),
|
|cx| cx.route("#"),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.add_item(nav::Item::link_disabled(
|
.with_item(nav::Item::link_disabled(
|
||||||
L10n::l("sample_menus_item_disabled"),
|
L10n::l("sample_menus_item_disabled"),
|
||||||
|cx| cx.route("#"),
|
|cx| cx.route("#"),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
.add_item(navbar::Item::nav(
|
.with_item(navbar::Item::nav(
|
||||||
Nav::new()
|
Nav::new()
|
||||||
.with_classes(
|
.with_classes(
|
||||||
ClassesOp::Add,
|
ClassesOp::Add,
|
||||||
classes::Margin::with(Side::Start, ScaleSize::Auto).to_class(),
|
classes::Margin::with(Side::Start, ScaleSize::Auto).to_class(),
|
||||||
)
|
)
|
||||||
.add_item(nav::Item::link(
|
.with_item(nav::Item::link(
|
||||||
L10n::l("sample_menus_item_sign_up"),
|
L10n::l("sample_menus_item_sign_up"),
|
||||||
|cx| cx.route("/auth/sign-up"),
|
|cx| cx.route("/auth/sign-up"),
|
||||||
))
|
))
|
||||||
.add_item(nav::Item::link(L10n::l("sample_menus_item_login"), |cx| {
|
.with_item(nav::Item::link(L10n::l("sample_menus_item_login"), |cx| {
|
||||||
cx.route("/auth/login")
|
cx.route("/auth/login")
|
||||||
})),
|
})),
|
||||||
));
|
));
|
||||||
|
|
@ -92,7 +92,7 @@ impl Extension for SuperMenu {
|
||||||
InRegion::Global(&DefaultRegion::Header).add(
|
InRegion::Global(&DefaultRegion::Header).add(
|
||||||
Container::new()
|
Container::new()
|
||||||
.with_width(container::Width::FluidMax(UnitValue::RelRem(75.0)))
|
.with_width(container::Width::FluidMax(UnitValue::RelRem(75.0)))
|
||||||
.add_child(navbar_menu),
|
.with_child(navbar_menu),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,10 @@ use pagetop_aliner::Aliner;
|
||||||
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||||
Page::new(request)
|
Page::new(request)
|
||||||
.with_theme(&Aliner)
|
.with_theme(&Aliner)
|
||||||
.add_child(
|
.with_child(
|
||||||
Block::new()
|
Block::new()
|
||||||
.with_title(L10n::l("sample_title"))
|
.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)) }
|
p { (L10n::l("sample_content").using(cx)) }
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
@ -122,7 +122,7 @@ impl Theme for Aliner {
|
||||||
))
|
))
|
||||||
.alter_child_in(
|
.alter_child_in(
|
||||||
&DefaultRegion::Footer,
|
&DefaultRegion::Footer,
|
||||||
ChildOp::AddIfEmpty(Child::with(PoweredBy::new())),
|
ChildOp::AddIfEmpty(PoweredBy::new().into()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,17 +150,11 @@ impl Container {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo componente hijo al contenedor.
|
/// Añade un nuevo componente al contenedor o modifica la lista de componentes (`children`) con
|
||||||
#[inline]
|
/// una operación [`ChildOp`].
|
||||||
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`].
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.children.alter_child(op);
|
self.children.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
//! .with_button_color(ButtonColor::Background(Color::Secondary))
|
//! .with_button_color(ButtonColor::Background(Color::Secondary))
|
||||||
//! .with_auto_close(dropdown::AutoClose::ClickableInside)
|
//! .with_auto_close(dropdown::AutoClose::ClickableInside)
|
||||||
//! .with_direction(dropdown::Direction::Dropend)
|
//! .with_direction(dropdown::Direction::Dropend)
|
||||||
//! .add_item(dropdown::Item::link(L10n::n("Home"), |_| "/".into()))
|
//! .with_item(dropdown::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
//! .add_item(dropdown::Item::link_blank(L10n::n("External"), |_| "https://google.es".into()))
|
//! .with_item(dropdown::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
|
||||||
//! .add_item(dropdown::Item::divider())
|
//! .with_item(dropdown::Item::divider())
|
||||||
//! .add_item(dropdown::Item::header(L10n::n("User session")))
|
//! .with_item(dropdown::Item::header(L10n::n("User session")))
|
||||||
//! .add_item(dropdown::Item::button(L10n::n("Sign out")));
|
//! .with_item(dropdown::Item::button(L10n::n("Sign out")));
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
mod props;
|
mod props;
|
||||||
|
|
|
||||||
|
|
@ -240,27 +240,22 @@ impl Dropdown {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo elemento hijo al menú.
|
/// Añade un nuevo elemento al menú o modifica la lista de elementos del menú con una operación
|
||||||
#[inline]
|
/// [`ChildOp`].
|
||||||
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`].
|
|
||||||
///
|
///
|
||||||
/// Para añadir elementos usa [`Child::with(item)`](Child::with):
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// dropdown.with_items(ChildOp::Add(Child::with(dropdown::Item::link(...))));
|
/// dropdown.with_item(dropdown::Item::link("Opción", "/ruta"));
|
||||||
/// dropdown.with_items(ChildOp::AddMany(vec![
|
/// dropdown.with_item(ChildOp::AddMany(vec![
|
||||||
/// Child::with(dropdown::Item::link(...)),
|
/// dropdown::Item::link(...).into(),
|
||||||
/// Child::with(dropdown::Item::divider()),
|
/// dropdown::Item::divider().into(),
|
||||||
|
/// dropdown::Item::link(...).into(),
|
||||||
/// ]));
|
/// ]));
|
||||||
/// ```
|
/// ```
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_items(mut self, op: ChildOp) -> Self {
|
pub fn with_item(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.items.alter_child(op);
|
self.items.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use crate::theme::form;
|
||||||
/// .with_action("/search")
|
/// .with_action("/search")
|
||||||
/// .with_method(form::Method::Get)
|
/// .with_method(form::Method::Get)
|
||||||
/// .with_classes(ClassesOp::Add, "mb-3")
|
/// .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)]
|
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||||
pub struct Form {
|
pub struct Form {
|
||||||
|
|
@ -114,17 +114,11 @@ impl Form {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo componente hijo al formulario.
|
/// Añade un nuevo componente al formulario o modifica la lista de de componentes (`children`)
|
||||||
#[inline]
|
/// con una operación [`ChildOp`].
|
||||||
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`].
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.children.alter_child(op);
|
self.children.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,17 +65,11 @@ impl Fieldset {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo componente hijo al `fieldset`.
|
/// Añade un nuevo componente al `fieldset` o modifica la lista de de componentes (`children`)
|
||||||
#[inline]
|
/// con una operación [`ChildOp`].
|
||||||
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`].
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.children.alter_child(op);
|
self.children.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@
|
||||||
//! # use pagetop_bootsier::prelude::*;
|
//! # use pagetop_bootsier::prelude::*;
|
||||||
//! let nav = Nav::tabs()
|
//! let nav = Nav::tabs()
|
||||||
//! .with_layout(nav::Layout::End)
|
//! .with_layout(nav::Layout::End)
|
||||||
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
//! .add_item(nav::Item::link_blank(L10n::n("External"), |_| "https://google.es".into()))
|
//! .with_item(nav::Item::link_blank(L10n::n("External"), |_| "https://docs.rs".into()))
|
||||||
//! .add_item(nav::Item::dropdown(
|
//! .with_item(nav::Item::dropdown(
|
||||||
//! Dropdown::new()
|
//! Dropdown::new()
|
||||||
//! .with_title(L10n::n("Options"))
|
//! .with_title(L10n::n("Options"))
|
||||||
//! .with_items(ChildOp::AddMany(vec![
|
//! .with_item(ChildOp::AddMany(vec![
|
||||||
//! Child::with(dropdown::Item::link(L10n::n("Action"), |_| "/action".into())),
|
//! dropdown::Item::link(L10n::n("Action"), |_| "/action".into()).into(),
|
||||||
//! Child::with(dropdown::Item::link(L10n::n("Another"), |_| "/another".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;
|
mod props;
|
||||||
|
|
|
||||||
|
|
@ -102,28 +102,21 @@ impl Nav {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo elemento hijo al menú.
|
/// Añade un nuevo elemento al menú o modifica la lista de elementos del menú con una operación
|
||||||
pub fn add_item(mut self, item: nav::Item) -> Self {
|
/// [`ChildOp`].
|
||||||
self.items.add(Child::with(item));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Modifica la lista de elementos del menú aplicando una operación [`ChildOp`].
|
|
||||||
///
|
///
|
||||||
/// Para añadir elementos usa [`Child::with(item)`](Child::with):
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// nav.with_items(ChildOp::Add(Child::with(nav::Item::link(...))));
|
/// nav.with_item(nav::Item::link("Inicio", "/"));
|
||||||
/// nav.with_items(ChildOp::AddMany(vec![
|
/// nav.with_item(ChildOp::AddMany(vec![
|
||||||
/// Child::with(nav::Item::link(...)),
|
/// nav::Item::link(...).into(),
|
||||||
/// Child::with(nav::Item::link_disabled(...)),
|
/// nav::Item::link_disabled(...).into(),
|
||||||
/// ]));
|
/// ]));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// Para la mayoría de los casos, [`add_item()`](Self::add_item) es más directo.
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_items(mut self, op: ChildOp) -> Self {
|
pub fn with_item(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.items.alter_child(op);
|
self.items.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@
|
||||||
//! # use pagetop::prelude::*;
|
//! # use pagetop::prelude::*;
|
||||||
//! # use pagetop_bootsier::prelude::*;
|
//! # use pagetop_bootsier::prelude::*;
|
||||||
//! let navbar = Navbar::simple()
|
//! let navbar = Navbar::simple()
|
||||||
//! .add_item(navbar::Item::nav(
|
//! .with_item(navbar::Item::nav(
|
||||||
//! Nav::new()
|
//! Nav::new()
|
||||||
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
//! .add_item(nav::Item::link(L10n::n("About"), |_| "/about".into()))
|
//! .with_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("Contact"), |_| "/contact".into()))
|
||||||
//! ));
|
//! ));
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
@ -30,11 +30,11 @@
|
||||||
//! # use pagetop_bootsier::prelude::*;
|
//! # use pagetop_bootsier::prelude::*;
|
||||||
//! let navbar = Navbar::simple_toggle()
|
//! let navbar = Navbar::simple_toggle()
|
||||||
//! .with_expand(BreakPoint::MD)
|
//! .with_expand(BreakPoint::MD)
|
||||||
//! .add_item(navbar::Item::nav(
|
//! .with_item(navbar::Item::nav(
|
||||||
//! Nav::new()
|
//! Nav::new()
|
||||||
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
//! .add_item(nav::Item::link_blank(L10n::n("Docs"), |_| "https://sample.com".into()))
|
//! .with_item(nav::Item::link_blank(L10n::n("Docs"), |_| "https://docs.rs".into()))
|
||||||
//! .add_item(nav::Item::link(L10n::n("Support"), |_| "/support".into()))
|
//! .with_item(nav::Item::link(L10n::n("Support"), |_| "/support".into()))
|
||||||
//! ));
|
//! ));
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
@ -48,20 +48,20 @@
|
||||||
//! .with_route(Some(|cx| cx.route("/")));
|
//! .with_route(Some(|cx| cx.route("/")));
|
||||||
//!
|
//!
|
||||||
//! let navbar = Navbar::brand_left(brand)
|
//! let navbar = Navbar::brand_left(brand)
|
||||||
//! .add_item(navbar::Item::nav(
|
//! .with_item(navbar::Item::nav(
|
||||||
//! Nav::new()
|
//! Nav::new()
|
||||||
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
//! .add_item(nav::Item::dropdown(
|
//! .with_item(nav::Item::dropdown(
|
||||||
//! Dropdown::new()
|
//! Dropdown::new()
|
||||||
//! .with_title(L10n::n("Tools"))
|
//! .with_title(L10n::n("Tools"))
|
||||||
//! .add_item(dropdown::Item::link(
|
//! .with_item(dropdown::Item::link(
|
||||||
//! L10n::n("Generator"), |_| "/tools/gen".into())
|
//! L10n::n("Generator"), |_| "/tools/gen".into())
|
||||||
//! )
|
//! )
|
||||||
//! .add_item(dropdown::Item::link(
|
//! .with_item(dropdown::Item::link(
|
||||||
//! L10n::n("Reports"), |_| "/tools/reports".into())
|
//! 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)
|
//! let navbar = Navbar::brand_right(brand)
|
||||||
//! .with_expand(BreakPoint::LG)
|
//! .with_expand(BreakPoint::LG)
|
||||||
//! .add_item(navbar::Item::nav(
|
//! .with_item(navbar::Item::nav(
|
||||||
//! Nav::pills()
|
//! Nav::pills()
|
||||||
//! .add_item(nav::Item::link(L10n::n("Dashboard"), |_| "/dashboard".into()))
|
//! .with_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("Users"), |_| "/users".into()))
|
||||||
//! ));
|
//! ));
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
@ -95,15 +95,15 @@
|
||||||
//! .with_backdrop(offcanvas::Backdrop::Enabled);
|
//! .with_backdrop(offcanvas::Backdrop::Enabled);
|
||||||
//!
|
//!
|
||||||
//! let navbar = Navbar::offcanvas(oc)
|
//! let navbar = Navbar::offcanvas(oc)
|
||||||
//! .add_item(navbar::Item::nav(
|
//! .with_item(navbar::Item::nav(
|
||||||
//! Nav::new()
|
//! Nav::new()
|
||||||
//! .add_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
//! .with_item(nav::Item::link(L10n::n("Home"), |_| "/".into()))
|
||||||
//! .add_item(nav::Item::link(L10n::n("Profile"), |_| "/profile".into()))
|
//! .with_item(nav::Item::link(L10n::n("Profile"), |_| "/profile".into()))
|
||||||
//! .add_item(nav::Item::dropdown(
|
//! .with_item(nav::Item::dropdown(
|
||||||
//! Dropdown::new()
|
//! Dropdown::new()
|
||||||
//! .with_title(L10n::n("More"))
|
//! .with_title(L10n::n("More"))
|
||||||
//! .add_item(dropdown::Item::link(L10n::n("Settings"), |_| "/settings".into()))
|
//! .with_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("Help"), |_| "/help".into()))
|
||||||
//! ))
|
//! ))
|
||||||
//! ));
|
//! ));
|
||||||
//! ```
|
//! ```
|
||||||
|
|
@ -119,11 +119,11 @@
|
||||||
//!
|
//!
|
||||||
//! let navbar = Navbar::brand_left(brand)
|
//! let navbar = Navbar::brand_left(brand)
|
||||||
//! .with_position(navbar::Position::FixedTop)
|
//! .with_position(navbar::Position::FixedTop)
|
||||||
//! .add_item(navbar::Item::nav(
|
//! .with_item(navbar::Item::nav(
|
||||||
//! Nav::new()
|
//! Nav::new()
|
||||||
//! .add_item(nav::Item::link(L10n::n("Dashboard"), |_| "/".into()))
|
//! .with_item(nav::Item::link(L10n::n("Dashboard"), |_| "/".into()))
|
||||||
//! .add_item(nav::Item::link(L10n::n("Donors"), |_| "/donors".into()))
|
//! .with_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("Stock"), |_| "/stock".into()))
|
||||||
//! ));
|
//! ));
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -255,29 +255,21 @@ impl Navbar {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo contenido hijo.
|
/// Añade un nuevo contenido a la barra de navegación o modifica la lista de contenidos de la
|
||||||
#[inline]
|
/// barra con una operación [`ChildOp`].
|
||||||
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`].
|
|
||||||
///
|
///
|
||||||
/// Para añadir elementos usa [`Child::with(item)`](Child::with):
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// navbar.with_items(ChildOp::Add(Child::with(navbar::Item::nav(...))));
|
/// navbar.with_item(navbar::Item::nav(...));
|
||||||
/// navbar.with_items(ChildOp::AddMany(vec![
|
/// navbar.with_item(ChildOp::AddMany(vec![
|
||||||
/// Child::with(navbar::Item::nav(...)),
|
/// navbar::Item::nav(...).into(),
|
||||||
/// Child::with(navbar::Item::text(...)),
|
/// navbar::Item::text(...).into(),
|
||||||
/// ]));
|
/// ]));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// Para la mayoría de los casos, [`add_item()`](Self::add_item) es más directo.
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_items(mut self, op: ChildOp) -> Self {
|
pub fn with_item(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.items.alter_child(op);
|
self.items.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@
|
||||||
//! .with_backdrop(offcanvas::Backdrop::Enabled)
|
//! .with_backdrop(offcanvas::Backdrop::Enabled)
|
||||||
//! .with_body_scroll(offcanvas::BodyScroll::Enabled)
|
//! .with_body_scroll(offcanvas::BodyScroll::Enabled)
|
||||||
//! .with_visibility(offcanvas::Visibility::Default)
|
//! .with_visibility(offcanvas::Visibility::Default)
|
||||||
//! .add_child(Dropdown::new()
|
//! .with_child(Dropdown::new()
|
||||||
//! .with_title(L10n::n("Menu"))
|
//! .with_title(L10n::n("Menu"))
|
||||||
//! .add_item(dropdown::Item::label(L10n::n("Label")))
|
//! .with_item(dropdown::Item::label(L10n::n("Label")))
|
||||||
//! .add_item(dropdown::Item::link_blank(L10n::n("Google"), |_| "https://google.es".into()))
|
//! .with_item(dropdown::Item::link_blank(L10n::n("Docs"), |_| "https://docs.rs".into()))
|
||||||
//! .add_item(dropdown::Item::link(L10n::n("Sign out"), |_| "/signout".into()))
|
//! .with_item(dropdown::Item::link(L10n::n("Sign out"), |_| "/signout".into()))
|
||||||
//! );
|
//! );
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,17 +135,11 @@ impl Offcanvas {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo componente hijo al panel.
|
/// Añade un nuevo componente al panel o modifica la lista de componentes (`children`) con una
|
||||||
#[inline]
|
/// operación [`ChildOp`].
|
||||||
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`].
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_children(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.children.alter_child(op);
|
self.children.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,17 +73,11 @@ impl Block {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo componente hijo al bloque.
|
/// Añade un nuevo componente al bloque o modifica la lista de componentes (`children`) con una
|
||||||
#[inline]
|
/// operación [`ChildOp`].
|
||||||
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`].
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.children.alter_child(op);
|
self.children.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,10 @@ pub enum IntroOpening {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use pagetop::prelude::*;
|
/// # use pagetop::prelude::*;
|
||||||
/// let intro = Intro::default()
|
/// let intro = Intro::default()
|
||||||
/// .add_child(
|
/// .with_child(
|
||||||
/// Block::new()
|
/// Block::new()
|
||||||
/// .with_title(L10n::l("intro_custom_block_title"))
|
/// .with_title(L10n::l("intro_custom_block_title"))
|
||||||
/// .add_child(Html::with(move |cx| {
|
/// .with_child(Html::with(move |cx| {
|
||||||
/// html! {
|
/// html! {
|
||||||
/// p { (L10n::l("intro_custom_paragraph_1").using(cx)) }
|
/// p { (L10n::l("intro_custom_paragraph_1").using(cx)) }
|
||||||
/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) }
|
/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) }
|
||||||
|
|
@ -277,19 +277,13 @@ impl Intro {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un nuevo componente hijo a la intro.
|
/// Añade un nuevo componente a la intro o modifica la lista de componentes (`children`) con una
|
||||||
|
/// operación [`ChildOp`].
|
||||||
///
|
///
|
||||||
/// Si es un bloque ([`Block`]) aplica estilos específicos para destacarlo.
|
/// Si se añade un bloque ([`Block`]) se aplicarán estilos específicos para destacarlo.
|
||||||
#[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`].
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.children.alter_child(op);
|
self.children.alter_child(op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,22 +30,22 @@ async fn home(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||||
|
|
||||||
Page::new(request)
|
Page::new(request)
|
||||||
.with_title(L10n::l("welcome_title"))
|
.with_title(L10n::l("welcome_title"))
|
||||||
.add_child(
|
.with_child(
|
||||||
Intro::new()
|
Intro::new()
|
||||||
.add_child(
|
.with_child(
|
||||||
Block::new()
|
Block::new()
|
||||||
.with_title(L10n::l("welcome_status_title"))
|
.with_title(L10n::l("welcome_status_title"))
|
||||||
.add_child(Html::with(move |cx| {
|
.with_child(Html::with(move |cx| {
|
||||||
html! {
|
html! {
|
||||||
p { (L10n::l("welcome_status_1").using(cx)) }
|
p { (L10n::l("welcome_status_1").using(cx)) }
|
||||||
p { (L10n::l("welcome_status_2").using(cx)) }
|
p { (L10n::l("welcome_status_2").using(cx)) }
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
.add_child(
|
.with_child(
|
||||||
Block::new()
|
Block::new()
|
||||||
.with_title(L10n::l("welcome_support_title"))
|
.with_title(L10n::l("welcome_support_title"))
|
||||||
.add_child(Html::with(move |cx| {
|
.with_child(Html::with(move |cx| {
|
||||||
html! {
|
html! {
|
||||||
p { (L10n::l("welcome_support_1").using(cx)) }
|
p { (L10n::l("welcome_support_1").using(cx)) }
|
||||||
p { (L10n::l("welcome_support_2").with_arg("app", app).using(cx)) }
|
p { (L10n::l("welcome_support_2").with_arg("app", app).using(cx)) }
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ impl Theme for Basic {
|
||||||
))
|
))
|
||||||
.alter_child_in(
|
.alter_child_in(
|
||||||
&DefaultRegion::Footer,
|
&DefaultRegion::Footer,
|
||||||
ChildOp::AddIfEmpty(Child::with(PoweredBy::new())),
|
ChildOp::AddIfEmpty(PoweredBy::new().into()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,9 @@ mod definition;
|
||||||
pub use definition::{Component, ComponentClone, ComponentRender};
|
pub use definition::{Component, ComponentClone, ComponentRender};
|
||||||
|
|
||||||
mod children;
|
mod children;
|
||||||
pub use children::Slot;
|
|
||||||
pub use children::Children;
|
pub use children::Children;
|
||||||
pub use children::ComponentGuard;
|
pub use children::ComponentGuard;
|
||||||
pub use children::{Child, ChildOp};
|
pub use children::{Child, ChildOp, Slot};
|
||||||
|
|
||||||
mod message;
|
mod message;
|
||||||
pub use message::{MessageLevel, StatusMessage};
|
pub use message::{MessageLevel, StatusMessage};
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,9 @@ impl<C: Component + 'static> From<Slot<C>> for Child {
|
||||||
/// Útil cuando se tiene un [`Slot`] y se necesita añadirlo a una lista [`Children`]:
|
/// Útil cuando se tiene un [`Slot`] y se necesita añadirlo a una lista [`Children`]:
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// children.add(Child::from(my_slot));
|
/// children.with_child(Child::from(my_slot));
|
||||||
/// // o equivalentemente:
|
/// // o equivalentemente:
|
||||||
/// children.add(my_slot.into());
|
/// children.with_child(my_slot.into());
|
||||||
/// ```
|
/// ```
|
||||||
fn from(typed: Slot<C>) -> Self {
|
fn from(typed: Slot<C>) -> Self {
|
||||||
if let Some(m) = typed.0 {
|
if let Some(m) = typed.0 {
|
||||||
|
|
@ -97,16 +97,33 @@ impl<T: Component + 'static> From<T> for Child {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Component + 'static> From<T> for ChildOp {
|
||||||
|
/// Convierte un componente en [`ChildOp::Add`], permitiendo pasar componentes directamente a
|
||||||
|
/// métodos como [`Children::with_child`] sin envolverlos explícitamente.
|
||||||
|
#[inline]
|
||||||
|
fn from(component: T) -> Self {
|
||||||
|
ChildOp::Add(Child::with(component))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Child> for ChildOp {
|
||||||
|
/// Convierte un [`Child`] en [`ChildOp::Add`].
|
||||||
|
#[inline]
|
||||||
|
fn from(child: Child) -> Self {
|
||||||
|
ChildOp::Add(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// *************************************************************************************************
|
// *************************************************************************************************
|
||||||
|
|
||||||
/// Variante tipada de [`Child`] para componentes con un tipo concreto conocido.
|
/// Contenedor tipado para un *único* componente de un tipo concreto conocido.
|
||||||
///
|
///
|
||||||
/// A diferencia de [`Child`], que encapsula cualquier componente como `dyn Component`, `Slot`
|
/// A diferencia de [`Child`], que encapsula cualquier componente como `dyn Component`, `Slot`
|
||||||
/// mantiene el tipo concreto `C` y permite acceder directamente a sus métodos específicos a través
|
/// mantiene el tipo concreto `C` y permite acceder directamente a sus métodos específicos a través
|
||||||
/// de [`get()`](Slot::get).
|
/// de [`get()`](Slot::get).
|
||||||
///
|
///
|
||||||
/// Se utiliza habitualmente para incrustar un componente dentro de otro cuando no se necesita una
|
/// Se usa habitualmente para incluir un componente dentro de otro cuando no se necesita una lista
|
||||||
/// lista completa de hijos ([`Children`]), sino un único componente tipado en un campo concreto.
|
/// completa de hijos ([`Children`]), sino un único componente tipado en un campo concreto.
|
||||||
#[derive(AutoDefault)]
|
#[derive(AutoDefault)]
|
||||||
pub struct Slot<C: Component>(Option<Mutex<C>>);
|
pub struct Slot<C: Component>(Option<Mutex<C>>);
|
||||||
|
|
||||||
|
|
@ -194,23 +211,59 @@ impl<C: Component> Slot<C> {
|
||||||
|
|
||||||
/// Operaciones para componentes hijo [`Child`] en una lista [`Children`].
|
/// Operaciones para componentes hijo [`Child`] en una lista [`Children`].
|
||||||
pub enum ChildOp {
|
pub enum ChildOp {
|
||||||
|
/// Añade un hijo al final de la lista.
|
||||||
Add(Child),
|
Add(Child),
|
||||||
|
/// Añade un hijo solo si la lista está vacía.
|
||||||
AddIfEmpty(Child),
|
AddIfEmpty(Child),
|
||||||
|
/// Añade varios hijos al final de la lista, en el orden recibido.
|
||||||
AddMany(Vec<Child>),
|
AddMany(Vec<Child>),
|
||||||
|
/// Inserta un hijo justo después del componente con el `id` dado, o al final si no existe.
|
||||||
InsertAfterId(&'static str, Child),
|
InsertAfterId(&'static str, Child),
|
||||||
|
/// Inserta un hijo justo antes del componente con el `id` dado, o al principio si no existe.
|
||||||
InsertBeforeId(&'static str, Child),
|
InsertBeforeId(&'static str, Child),
|
||||||
|
/// Inserta un hijo al principio de la lista.
|
||||||
Prepend(Child),
|
Prepend(Child),
|
||||||
|
/// Inserta varios hijos al principio de la lista, manteniendo el orden recibido.
|
||||||
PrependMany(Vec<Child>),
|
PrependMany(Vec<Child>),
|
||||||
|
/// Elimina el primer hijo con el `id` dado.
|
||||||
RemoveById(&'static str),
|
RemoveById(&'static str),
|
||||||
|
/// Sustituye el primer hijo con el `id` dado por otro componente.
|
||||||
ReplaceById(&'static str, Child),
|
ReplaceById(&'static str, Child),
|
||||||
|
/// Vacía la lista eliminando todos los hijos.
|
||||||
Reset,
|
Reset,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lista ordenada de componentes hijo ([`Child`]) mantenida por un componente padre.
|
/// Lista ordenada de componentes hijo ([`Child`]) mantenida por un componente padre.
|
||||||
///
|
///
|
||||||
/// Esta lista permite añadir, modificar, renderizar y consultar componentes hijo en orden de
|
/// Permite añadir, modificar, renderizar y consultar componentes hijo en orden de inserción, con
|
||||||
/// inserción, soportando operaciones avanzadas como inserción relativa o reemplazo por
|
/// soporte para operaciones avanzadas como inserción relativa o reemplazo por identificador a
|
||||||
/// identificador.
|
/// través de [`ChildOp`].
|
||||||
|
///
|
||||||
|
/// Los tipos que completan este sistema son:
|
||||||
|
///
|
||||||
|
/// - [`Child`]: representa un componente hijo encapsulado dentro de la lista. Almacena cualquier
|
||||||
|
/// componente sin necesidad de conocer su tipo concreto.
|
||||||
|
/// - [`Slot<C>`]: contenedor tipado para un *único* componente de tipo `C`. Preferible a `Children`
|
||||||
|
/// cuando el padre solo necesita un componente y quiere acceso directo a los métodos de `C`.
|
||||||
|
/// - [`ChildOp`]: operaciones disponibles sobre la lista. Cuando se necesita algo más que añadir al
|
||||||
|
/// final, se construye la variante adecuada y se pasa a [`with_child`](Self::with_child).
|
||||||
|
/// - [`ComponentGuard`]: devuelto por [`Slot::get`] para garantizar acceso exclusivo al componente
|
||||||
|
/// tipado. Mientras está activo bloquea cualquier otro acceso por lo que conviene liberarlo
|
||||||
|
/// cuanto antes.
|
||||||
|
///
|
||||||
|
/// # Conversiones implícitas
|
||||||
|
///
|
||||||
|
/// Cualquier componente implementa `Into<ChildOp>` (equivalente a `ChildOp::Add`) e `Into<Child>`.
|
||||||
|
/// Gracias a esto, [`with_child`](Self::with_child) acepta un componente directamente o cualquier
|
||||||
|
/// variante de [`ChildOp`]:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// // Añadir al final de la lista (implícito):
|
||||||
|
/// children.with_child(MiComponente::new());
|
||||||
|
///
|
||||||
|
/// // Operación explícita:
|
||||||
|
/// children.with_child(ChildOp::Prepend(MiComponente::new().into()));
|
||||||
|
/// ```
|
||||||
#[derive(AutoDefault, Clone, Debug)]
|
#[derive(AutoDefault, Clone, Debug)]
|
||||||
pub struct Children(Vec<Child>);
|
pub struct Children(Vec<Child>);
|
||||||
|
|
||||||
|
|
@ -222,15 +275,15 @@ impl Children {
|
||||||
|
|
||||||
/// Crea una lista con un componente hijo inicial.
|
/// Crea una lista con un componente hijo inicial.
|
||||||
pub fn with(child: Child) -> Self {
|
pub fn with(child: Child) -> Self {
|
||||||
Self::default().with_child(ChildOp::Add(child))
|
Self::default().with_child(child)
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Children BUILDER >***********************************************************************
|
// **< Children BUILDER >***********************************************************************
|
||||||
|
|
||||||
/// Ejecuta una operación con [`ChildOp`] en la lista.
|
/// Añade un componente hijo o aplica una operación [`ChildOp`] sobre la lista.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child(mut self, op: ChildOp) -> Self {
|
pub fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
match op {
|
match op.into() {
|
||||||
ChildOp::Add(any) => self.add(any),
|
ChildOp::Add(any) => self.add(any),
|
||||||
ChildOp::AddIfEmpty(any) => self.add_if_empty(any),
|
ChildOp::AddIfEmpty(any) => self.add_if_empty(any),
|
||||||
ChildOp::AddMany(many) => self.add_many(many),
|
ChildOp::AddMany(many) => self.add_many(many),
|
||||||
|
|
@ -245,17 +298,15 @@ impl Children {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un componente hijo al final de la lista.
|
/// Añade un componente hijo al final de la lista.
|
||||||
///
|
|
||||||
/// Es un atajo para `children.alter_child(ChildOp::Add(child))`.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add(&mut self, child: Child) -> &mut Self {
|
pub(crate) fn add(&mut self, child: Child) -> &mut Self {
|
||||||
self.0.push(child);
|
self.0.push(child);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un componente hijo en la lista sólo si está vacía.
|
/// Añade un componente hijo en la lista sólo si está vacía.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_if_empty(&mut self, child: Child) -> &mut Self {
|
pub(crate) fn add_if_empty(&mut self, child: Child) -> &mut Self {
|
||||||
if self.0.is_empty() {
|
if self.0.is_empty() {
|
||||||
self.0.push(child);
|
self.0.push(child);
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +387,7 @@ impl Children {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserta un hijo al principio de la colección.
|
/// Inserta un hijo al principio de la lista.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn prepend(&mut self, child: Child) -> &mut Self {
|
fn prepend(&mut self, child: Child) -> &mut Self {
|
||||||
self.0.insert(0, child);
|
self.0.insert(0, child);
|
||||||
|
|
@ -391,7 +442,7 @@ impl IntoIterator for Children {
|
||||||
|
|
||||||
/// Consume la estructura `Children`, devolviendo un iterador que consume los elementos.
|
/// Consume la estructura `Children`, devolviendo un iterador que consume los elementos.
|
||||||
///
|
///
|
||||||
/// # Ejemplo de uso:
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// let children = Children::new().with(child1).with(child2);
|
/// let children = Children::new().with(child1).with(child2);
|
||||||
|
|
@ -410,7 +461,7 @@ impl<'a> IntoIterator for &'a Children {
|
||||||
|
|
||||||
/// Itera sobre una referencia inmutable de `Children`, devolviendo un iterador de referencia.
|
/// Itera sobre una referencia inmutable de `Children`, devolviendo un iterador de referencia.
|
||||||
///
|
///
|
||||||
/// # Ejemplo de uso:
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// let children = Children::new().with(child1).with(child2);
|
/// let children = Children::new().with(child1).with(child2);
|
||||||
|
|
@ -429,7 +480,7 @@ impl<'a> IntoIterator for &'a mut Children {
|
||||||
|
|
||||||
/// Itera sobre una referencia mutable de `Children`, devolviendo un iterador mutable.
|
/// Itera sobre una referencia mutable de `Children`, devolviendo un iterador mutable.
|
||||||
///
|
///
|
||||||
/// # Ejemplo de uso:
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// let mut children = Children::new().with(child1).with(child2);
|
/// let mut children = Children::new().with(child1).with(child2);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::component::{ChildOp, MessageLevel, StatusMessage};
|
use crate::core::component::{ChildOp, MessageLevel, StatusMessage};
|
||||||
use crate::core::theme::all::DEFAULT_THEME;
|
use crate::core::theme::all::DEFAULT_THEME;
|
||||||
use crate::core::theme::{ChildrenInRegions, RegionRef, TemplateRef, ThemeRef};
|
use crate::core::theme::{ChildrenInRegions, DefaultRegion, RegionRef, TemplateRef, ThemeRef};
|
||||||
use crate::core::TypeInfo;
|
use crate::core::TypeInfo;
|
||||||
use crate::html::{html, Markup, RoutePath};
|
use crate::html::{html, Markup, RoutePath};
|
||||||
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
||||||
|
|
@ -137,9 +137,15 @@ pub trait Contextual: LangId {
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
fn with_assets(self, op: AssetsOp) -> Self;
|
fn with_assets(self, op: AssetsOp) -> Self;
|
||||||
|
|
||||||
/// Opera con [`ChildOp`] en una región del documento.
|
/// Añade un componente o aplica una operación [`ChildOp`] en la región por defecto del
|
||||||
|
/// documento.
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
fn with_child_in(self, region_ref: RegionRef, op: ChildOp) -> Self;
|
fn with_child(self, op: impl Into<ChildOp>) -> Self;
|
||||||
|
|
||||||
|
/// Añade un componente o aplica una operación [`ChildOp`] en una región específica del
|
||||||
|
/// documento.
|
||||||
|
#[builder_fn]
|
||||||
|
fn with_child_in(self, region_ref: RegionRef, op: impl Into<ChildOp>) -> Self;
|
||||||
|
|
||||||
// **< Contextual GETTERS >*********************************************************************
|
// **< Contextual GETTERS >*********************************************************************
|
||||||
|
|
||||||
|
|
@ -557,8 +563,15 @@ impl Contextual for Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
fn with_child_in(mut self, region_ref: RegionRef, op: ChildOp) -> Self {
|
fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.regions.alter_child_in(region_ref, op);
|
self.regions
|
||||||
|
.alter_child_in(&DefaultRegion::Content, op.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[builder_fn]
|
||||||
|
fn with_child_in(mut self, region_ref: RegionRef, op: impl Into<ChildOp>) -> Self {
|
||||||
|
self.regions.alter_child_in(region_ref, op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::base::component::{Html, Intro, IntroOpening};
|
use crate::base::component::{Html, Intro, IntroOpening};
|
||||||
use crate::core::component::{Child, ChildOp, Component, ComponentError, Context, Contextual};
|
use crate::core::component::{ChildOp, Component, ComponentError, Context, Contextual};
|
||||||
use crate::core::extension::Extension;
|
use crate::core::extension::Extension;
|
||||||
use crate::core::theme::{DefaultRegion, DefaultTemplate, TemplateRef};
|
use crate::core::theme::{DefaultRegion, DefaultTemplate, TemplateRef};
|
||||||
use crate::global;
|
use crate::global;
|
||||||
|
|
@ -247,14 +247,17 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
.alter_template(&DefaultTemplate::Error)
|
.alter_template(&DefaultTemplate::Error)
|
||||||
.alter_child_in(
|
.alter_child_in(
|
||||||
&DefaultRegion::Content,
|
&DefaultRegion::Content,
|
||||||
ChildOp::Prepend(Child::with(Html::with(move |cx| {
|
ChildOp::Prepend(
|
||||||
html! {
|
Html::with(move |cx| {
|
||||||
div {
|
html! {
|
||||||
h1 { (L10n::l("error403_alert").using(cx)) }
|
div {
|
||||||
p { (L10n::l("error403_help").using(cx)) }
|
h1 { (L10n::l("error403_alert").using(cx)) }
|
||||||
|
p { (L10n::l("error403_help").using(cx)) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}))),
|
.into(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,14 +273,17 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
.alter_template(&DefaultTemplate::Error)
|
.alter_template(&DefaultTemplate::Error)
|
||||||
.alter_child_in(
|
.alter_child_in(
|
||||||
&DefaultRegion::Content,
|
&DefaultRegion::Content,
|
||||||
ChildOp::Prepend(Child::with(Html::with(move |cx| {
|
ChildOp::Prepend(
|
||||||
html! {
|
Html::with(move |cx| {
|
||||||
div {
|
html! {
|
||||||
h1 { (L10n::l("error404_alert").using(cx)) }
|
div {
|
||||||
p { (L10n::l("error404_help").using(cx)) }
|
h1 { (L10n::l("error404_alert").using(cx)) }
|
||||||
|
p { (L10n::l("error404_help").using(cx)) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}))),
|
.into(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -300,19 +306,20 @@ pub trait Theme: Extension + Send + Sync {
|
||||||
.alter_template(&DefaultTemplate::Error)
|
.alter_template(&DefaultTemplate::Error)
|
||||||
.alter_child_in(
|
.alter_child_in(
|
||||||
&DefaultRegion::Content,
|
&DefaultRegion::Content,
|
||||||
ChildOp::Prepend(Child::with(
|
ChildOp::Prepend(
|
||||||
Intro::new()
|
Intro::new()
|
||||||
.with_title(L10n::l("error_code").with_arg("code", code.to_string()))
|
.with_title(L10n::l("error_code").with_arg("code", code.to_string()))
|
||||||
.with_slogan(L10n::n(code.to_string()))
|
.with_slogan(L10n::n(code.to_string()))
|
||||||
.with_button(None)
|
.with_button(None)
|
||||||
.with_opening(IntroOpening::Custom)
|
.with_opening(IntroOpening::Custom)
|
||||||
.add_child(Html::with(move |cx| {
|
.with_child(Html::with(move |cx| {
|
||||||
html! {
|
html! {
|
||||||
h1 { (alert.using(cx)) }
|
h1 { (alert.using(cx)) }
|
||||||
p { (help.using(cx)) }
|
p { (help.using(cx)) }
|
||||||
}
|
}
|
||||||
})),
|
}))
|
||||||
)),
|
.into(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,16 +44,19 @@ pub(crate) struct ChildrenInRegions(HashMap<String, Children>);
|
||||||
|
|
||||||
impl ChildrenInRegions {
|
impl ChildrenInRegions {
|
||||||
pub fn with(region_ref: RegionRef, child: Child) -> Self {
|
pub fn with(region_ref: RegionRef, child: Child) -> Self {
|
||||||
Self::default().with_child_in(region_ref, ChildOp::Add(child))
|
Self::default().with_child_in(region_ref, child)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
pub fn with_child_in(mut self, region_ref: RegionRef, op: ChildOp) -> Self {
|
pub fn with_child_in(mut self, region_ref: RegionRef, op: impl Into<ChildOp>) -> Self {
|
||||||
|
let child = op.into();
|
||||||
if let Some(region) = self.0.get_mut(region_ref.name()) {
|
if let Some(region) = self.0.get_mut(region_ref.name()) {
|
||||||
region.alter_child(op);
|
region.alter_child(child);
|
||||||
} else {
|
} else {
|
||||||
self.0
|
self.0.insert(
|
||||||
.insert(region_ref.name().to_owned(), Children::new().with_child(op));
|
region_ref.name().to_owned(),
|
||||||
|
Children::new().with_child(child),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ impl Extension for HelloWorld {
|
||||||
|
|
||||||
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
|
||||||
Page::new(request)
|
Page::new(request)
|
||||||
.add_child(Html::with(|_| html! { h1 { "Hello World!" } }))
|
.with_child(Html::with(|_| html! { h1 { "Hello World!" } }))
|
||||||
.render()
|
.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ pub use error::ErrorPage;
|
||||||
pub use actix_web::Result as ResultPage;
|
pub use actix_web::Result as ResultPage;
|
||||||
|
|
||||||
use crate::base::action;
|
use crate::base::action;
|
||||||
use crate::core::component::{AssetsOp, Context, Contextual};
|
use crate::core::component::{AssetsOp, ChildOp, Context, Contextual};
|
||||||
use crate::core::component::{Child, ChildOp, Component};
|
|
||||||
use crate::core::theme::{DefaultRegion, Region, RegionRef, TemplateRef, ThemeRef};
|
use crate::core::theme::{DefaultRegion, Region, RegionRef, TemplateRef, ThemeRef};
|
||||||
use crate::html::{html, Markup, DOCTYPE};
|
use crate::html::{html, Markup, DOCTYPE};
|
||||||
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
use crate::html::{Assets, Favicon, JavaScript, StyleSheet};
|
||||||
|
|
@ -160,22 +159,6 @@ impl Page {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Añade un componente hijo a la región de contenido por defecto.
|
|
||||||
pub fn add_child(mut self, component: impl Component) -> Self {
|
|
||||||
self.context.alter_child_in(
|
|
||||||
&DefaultRegion::Content,
|
|
||||||
ChildOp::Add(Child::with(component)),
|
|
||||||
);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Añade un componente hijo en la región `region_name` de la página.
|
|
||||||
pub fn add_child_in(mut self, region_ref: RegionRef, component: impl Component) -> Self {
|
|
||||||
self.context
|
|
||||||
.alter_child_in(region_ref, ChildOp::Add(Child::with(component)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// **< Page GETTERS >***************************************************************************
|
// **< Page GETTERS >***************************************************************************
|
||||||
|
|
||||||
/// Devuelve el título traducido para el idioma de la página, si existe.
|
/// Devuelve el título traducido para el idioma de la página, si existe.
|
||||||
|
|
@ -340,8 +323,15 @@ impl Contextual for Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[builder_fn]
|
#[builder_fn]
|
||||||
fn with_child_in(mut self, region_ref: RegionRef, op: ChildOp) -> Self {
|
fn with_child(mut self, op: impl Into<ChildOp>) -> Self {
|
||||||
self.context.alter_child_in(region_ref, op);
|
self.context
|
||||||
|
.alter_child_in(&DefaultRegion::Content, op.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[builder_fn]
|
||||||
|
fn with_child_in(mut self, region_ref: RegionRef, op: impl Into<ChildOp>) -> Self {
|
||||||
|
self.context.alter_child_in(region_ref, op.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,10 @@ async fn child_id_returns_component_id() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn child_from_component_is_equivalent_to_with() {
|
async fn child_from_component_is_equivalent_to_with() {
|
||||||
let child: Child = TestComp::text("desde from").into();
|
let child: Child = TestComp::text("desde from").into();
|
||||||
assert_eq!(child.render(&mut Context::default()).into_string(), "desde from");
|
assert_eq!(
|
||||||
|
child.render(&mut Context::default()).into_string(),
|
||||||
|
"desde from"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
|
|
@ -74,8 +77,14 @@ async fn child_clone_is_deep() {
|
||||||
// Modificar el clon no debe afectar al original.
|
// Modificar el clon no debe afectar al original.
|
||||||
let original = Child::with(TestComp::text("original"));
|
let original = Child::with(TestComp::text("original"));
|
||||||
let clone = original.clone();
|
let clone = original.clone();
|
||||||
assert_eq!(original.render(&mut Context::default()).into_string(), "original");
|
assert_eq!(
|
||||||
assert_eq!(clone.render(&mut Context::default()).into_string(), "original");
|
original.render(&mut Context::default()).into_string(),
|
||||||
|
"original"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
clone.render(&mut Context::default()).into_string(),
|
||||||
|
"original"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Children + ChildOp >*************************************************************************
|
// **< Children + ChildOp >*************************************************************************
|
||||||
|
|
@ -90,9 +99,9 @@ async fn children_new_is_empty() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_add_appends_in_order() {
|
async fn children_add_appends_in_order() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("a"))))
|
.with_child(TestComp::text("a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("b"))))
|
.with_child(TestComp::text("b"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("c"))));
|
.with_child(TestComp::text("c"));
|
||||||
assert_eq!(c.len(), 3);
|
assert_eq!(c.len(), 3);
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
||||||
}
|
}
|
||||||
|
|
@ -102,12 +111,11 @@ async fn children_add_if_empty_only_adds_when_list_is_empty() {
|
||||||
let mut cx = Context::default();
|
let mut cx = Context::default();
|
||||||
|
|
||||||
// Se añade porque la lista está vacía.
|
// Se añade porque la lista está vacía.
|
||||||
let c = Children::new()
|
let c = Children::new().with_child(ChildOp::AddIfEmpty(TestComp::text("primero").into()));
|
||||||
.with_child(ChildOp::AddIfEmpty(Child::with(TestComp::text("primero"))));
|
|
||||||
assert_eq!(c.len(), 1);
|
assert_eq!(c.len(), 1);
|
||||||
|
|
||||||
// No se añade porque ya hay un elemento.
|
// No se añade porque ya hay un elemento.
|
||||||
let c = c.with_child(ChildOp::AddIfEmpty(Child::with(TestComp::text("segundo"))));
|
let c = c.with_child(ChildOp::AddIfEmpty(TestComp::text("segundo").into()));
|
||||||
assert_eq!(c.len(), 1);
|
assert_eq!(c.len(), 1);
|
||||||
assert_eq!(c.render(&mut cx).into_string(), "primero");
|
assert_eq!(c.render(&mut cx).into_string(), "primero");
|
||||||
}
|
}
|
||||||
|
|
@ -115,9 +123,9 @@ async fn children_add_if_empty_only_adds_when_list_is_empty() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_add_many_appends_all_in_order() {
|
async fn children_add_many_appends_all_in_order() {
|
||||||
let c = Children::new().with_child(ChildOp::AddMany(vec![
|
let c = Children::new().with_child(ChildOp::AddMany(vec![
|
||||||
Child::with(TestComp::text("x")),
|
TestComp::text("x").into(),
|
||||||
Child::with(TestComp::text("y")),
|
TestComp::text("y").into(),
|
||||||
Child::with(TestComp::text("z")),
|
TestComp::text("z").into(),
|
||||||
]));
|
]));
|
||||||
assert_eq!(c.len(), 3);
|
assert_eq!(c.len(), 3);
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "xyz");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "xyz");
|
||||||
|
|
@ -126,18 +134,18 @@ async fn children_add_many_appends_all_in_order() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_prepend_inserts_at_start() {
|
async fn children_prepend_inserts_at_start() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("b"))))
|
.with_child(TestComp::text("b"))
|
||||||
.with_child(ChildOp::Prepend(Child::with(TestComp::text("a"))));
|
.with_child(ChildOp::Prepend(TestComp::text("a").into()));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "ab");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "ab");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_prepend_many_inserts_all_at_start_maintaining_order() {
|
async fn children_prepend_many_inserts_all_at_start_maintaining_order() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("c"))))
|
.with_child(TestComp::text("c"))
|
||||||
.with_child(ChildOp::PrependMany(vec![
|
.with_child(ChildOp::PrependMany(vec![
|
||||||
Child::with(TestComp::text("a")),
|
TestComp::text("a").into(),
|
||||||
Child::with(TestComp::text("b")),
|
TestComp::text("b").into(),
|
||||||
]));
|
]));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
||||||
}
|
}
|
||||||
|
|
@ -145,43 +153,49 @@ async fn children_prepend_many_inserts_all_at_start_maintaining_order() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_insert_after_id_inserts_after_matching_element() {
|
async fn children_insert_after_id_inserts_after_matching_element() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("first", "a"))))
|
.with_child(TestComp::tagged("first", "a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("c"))))
|
.with_child(TestComp::text("c"))
|
||||||
.with_child(ChildOp::InsertAfterId("first", Child::with(TestComp::text("b"))));
|
.with_child(ChildOp::InsertAfterId("first", TestComp::text("b").into()));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_insert_after_id_appends_when_id_not_found() {
|
async fn children_insert_after_id_appends_when_id_not_found() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("a"))))
|
.with_child(TestComp::text("a"))
|
||||||
.with_child(ChildOp::InsertAfterId("no-existe", Child::with(TestComp::text("b"))));
|
.with_child(ChildOp::InsertAfterId(
|
||||||
|
"no-existe",
|
||||||
|
TestComp::text("b").into(),
|
||||||
|
));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "ab");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "ab");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_insert_before_id_inserts_before_matching_element() {
|
async fn children_insert_before_id_inserts_before_matching_element() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("a"))))
|
.with_child(TestComp::text("a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("last", "c"))))
|
.with_child(TestComp::tagged("last", "c"))
|
||||||
.with_child(ChildOp::InsertBeforeId("last", Child::with(TestComp::text("b"))));
|
.with_child(ChildOp::InsertBeforeId("last", TestComp::text("b").into()));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_insert_before_id_prepends_when_id_not_found() {
|
async fn children_insert_before_id_prepends_when_id_not_found() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("b"))))
|
.with_child(TestComp::text("b"))
|
||||||
.with_child(ChildOp::InsertBeforeId("no-existe", Child::with(TestComp::text("a"))));
|
.with_child(ChildOp::InsertBeforeId(
|
||||||
|
"no-existe",
|
||||||
|
TestComp::text("a").into(),
|
||||||
|
));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "ab");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "ab");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_remove_by_id_removes_first_matching_element() {
|
async fn children_remove_by_id_removes_first_matching_element() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("keep", "a"))))
|
.with_child(TestComp::tagged("keep", "a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("drop", "b"))))
|
.with_child(TestComp::tagged("drop", "b"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("c"))))
|
.with_child(TestComp::text("c"))
|
||||||
.with_child(ChildOp::RemoveById("drop"));
|
.with_child(ChildOp::RemoveById("drop"));
|
||||||
assert_eq!(c.len(), 2);
|
assert_eq!(c.len(), 2);
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "ac");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "ac");
|
||||||
|
|
@ -190,7 +204,7 @@ async fn children_remove_by_id_removes_first_matching_element() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_remove_by_id_does_nothing_when_id_not_found() {
|
async fn children_remove_by_id_does_nothing_when_id_not_found() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("a"))))
|
.with_child(TestComp::text("a"))
|
||||||
.with_child(ChildOp::RemoveById("no-existe"));
|
.with_child(ChildOp::RemoveById("no-existe"));
|
||||||
assert_eq!(c.len(), 1);
|
assert_eq!(c.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
@ -198,11 +212,11 @@ async fn children_remove_by_id_does_nothing_when_id_not_found() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_replace_by_id_replaces_first_matching_element() {
|
async fn children_replace_by_id_replaces_first_matching_element() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("target", "viejo"))))
|
.with_child(TestComp::tagged("target", "viejo"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("b"))))
|
.with_child(TestComp::text("b"))
|
||||||
.with_child(ChildOp::ReplaceById(
|
.with_child(ChildOp::ReplaceById(
|
||||||
"target",
|
"target",
|
||||||
Child::with(TestComp::text("nuevo")),
|
TestComp::text("nuevo").into(),
|
||||||
));
|
));
|
||||||
assert_eq!(c.len(), 2);
|
assert_eq!(c.len(), 2);
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "nuevob");
|
assert_eq!(c.render(&mut Context::default()).into_string(), "nuevob");
|
||||||
|
|
@ -211,8 +225,8 @@ async fn children_replace_by_id_replaces_first_matching_element() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_reset_clears_all_elements() {
|
async fn children_reset_clears_all_elements() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("a"))))
|
.with_child(TestComp::text("a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("b"))))
|
.with_child(TestComp::text("b"))
|
||||||
.with_child(ChildOp::Reset);
|
.with_child(ChildOp::Reset);
|
||||||
assert!(c.is_empty());
|
assert!(c.is_empty());
|
||||||
}
|
}
|
||||||
|
|
@ -220,8 +234,8 @@ async fn children_reset_clears_all_elements() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_get_by_id_returns_first_matching_child() {
|
async fn children_get_by_id_returns_first_matching_child() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("uno", "a"))))
|
.with_child(TestComp::tagged("uno", "a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("dos", "b"))));
|
.with_child(TestComp::tagged("dos", "b"));
|
||||||
assert!(c.get_by_id("uno").is_some());
|
assert!(c.get_by_id("uno").is_some());
|
||||||
assert!(c.get_by_id("dos").is_some());
|
assert!(c.get_by_id("dos").is_some());
|
||||||
assert!(c.get_by_id("tres").is_none());
|
assert!(c.get_by_id("tres").is_none());
|
||||||
|
|
@ -230,9 +244,9 @@ async fn children_get_by_id_returns_first_matching_child() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_iter_by_id_yields_all_matching_children() {
|
async fn children_iter_by_id_yields_all_matching_children() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("rep", "a"))))
|
.with_child(TestComp::tagged("rep", "a"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("rep", "b"))))
|
.with_child(TestComp::tagged("rep", "b"))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::tagged("otro", "c"))));
|
.with_child(TestComp::tagged("otro", "c"));
|
||||||
assert_eq!(c.iter_by_id("rep").count(), 2);
|
assert_eq!(c.iter_by_id("rep").count(), 2);
|
||||||
assert_eq!(c.iter_by_id("otro").count(), 1);
|
assert_eq!(c.iter_by_id("otro").count(), 1);
|
||||||
assert_eq!(c.iter_by_id("ninguno").count(), 0);
|
assert_eq!(c.iter_by_id("ninguno").count(), 0);
|
||||||
|
|
@ -241,10 +255,13 @@ async fn children_iter_by_id_yields_all_matching_children() {
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn children_render_concatenates_all_outputs_in_order() {
|
async fn children_render_concatenates_all_outputs_in_order() {
|
||||||
let c = Children::new()
|
let c = Children::new()
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("uno "))))
|
.with_child(TestComp::text("uno "))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("dos "))))
|
.with_child(TestComp::text("dos "))
|
||||||
.with_child(ChildOp::Add(Child::with(TestComp::text("tres"))));
|
.with_child(TestComp::text("tres"));
|
||||||
assert_eq!(c.render(&mut Context::default()).into_string(), "uno dos tres");
|
assert_eq!(
|
||||||
|
c.render(&mut Context::default()).into_string(),
|
||||||
|
"uno dos tres"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// **< Slot >****************************************************************************************
|
// **< Slot >****************************************************************************************
|
||||||
|
|
@ -261,7 +278,10 @@ async fn slot_default_is_empty() {
|
||||||
async fn slot_with_stores_component() {
|
async fn slot_with_stores_component() {
|
||||||
let slot = Slot::with(TestComp::text("contenido"));
|
let slot = Slot::with(TestComp::text("contenido"));
|
||||||
assert!(slot.get().is_some());
|
assert!(slot.get().is_some());
|
||||||
assert_eq!(slot.render(&mut Context::default()).into_string(), "contenido");
|
assert_eq!(
|
||||||
|
slot.render(&mut Context::default()).into_string(),
|
||||||
|
"contenido"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
|
|
@ -290,9 +310,12 @@ async fn slot_get_allows_mutating_component() {
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
async fn slot_with_component_replaces_content() {
|
async fn slot_with_component_replaces_content() {
|
||||||
let slot = Slot::with(TestComp::text("primero"))
|
let slot =
|
||||||
.with_component(Some(TestComp::text("segundo")));
|
Slot::with(TestComp::text("primero")).with_component(Some(TestComp::text("segundo")));
|
||||||
assert_eq!(slot.render(&mut Context::default()).into_string(), "segundo");
|
assert_eq!(
|
||||||
|
slot.render(&mut Context::default()).into_string(),
|
||||||
|
"segundo"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pagetop::test]
|
#[pagetop::test]
|
||||||
|
|
@ -318,5 +341,8 @@ async fn slot_clone_is_deep() {
|
||||||
async fn slot_converts_into_child() {
|
async fn slot_converts_into_child() {
|
||||||
let slot = Slot::with(TestComp::text("desde slot"));
|
let slot = Slot::with(TestComp::text("desde slot"));
|
||||||
let child = Child::from(slot);
|
let child = Child::from(slot);
|
||||||
assert_eq!(child.render(&mut Context::default()).into_string(), "desde slot");
|
assert_eq!(
|
||||||
|
child.render(&mut Context::default()).into_string(),
|
||||||
|
"desde slot"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue