Compare commits

..

4 commits

20 changed files with 72 additions and 63 deletions

View file

@ -25,7 +25,7 @@ internos pueden omitirse si no afectan al uso del proyecto.
- [context] Generaliza los parámetros de contexto
- [context] Define un `trait` común de contexto
- Modifica tipos para atributos HTML a minúsculas
- Renombra `with_component` por `add_component`
- Renombra `with_component` por `add_child`
### Corregido

1
Cargo.lock generated
View file

@ -1593,6 +1593,7 @@ version = "0.0.18"
dependencies = [
"pagetop",
"pagetop-build",
"serde",
]
[[package]]

View file

@ -39,7 +39,7 @@ actix-web = { workspace = true, default-features = true }
actix-session = { version = "0.11", features = ["cookie-session"] }
actix-web-files = { package = "actix-files", version = "0.6" }
serde = { version = "1.0", features = ["derive"] }
serde.workspace = true
pagetop-macros.workspace = true
pagetop-statics.workspace = true
@ -78,6 +78,7 @@ authors = ["Manuel Cillero <manuel@cillero.es>"]
[workspace.dependencies]
actix-web = { version = "4.11", default-features = false }
serde = { version = "1.0", features = ["derive"] }
# Helpers
pagetop-build = { version = "0.3", path = "helpers/pagetop-build" }
pagetop-macros = { version = "0.2", path = "helpers/pagetop-macros" }

View file

@ -60,7 +60,7 @@ impl Extension for HelloWorld {
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.add_component(Html::with(move |_| html! { h1 { "Hello World!" } }))
.add_child(Html::with(|_| html! { h1 { "Hello World!" } }))
.render()
}
@ -102,7 +102,7 @@ El código se organiza en un *workspace* donde actualmente se incluyen los sigui
es un tema para demos y pruebas que muestra esquemáticamente la composición de las páginas HTML.
* **[pagetop-bootsier](https://git.cillero.es/manuelcillero/pagetop/src/branch/main/extensions/pagetop-bootsier)**,
tema basado en [Bootstrap](https://getbootstrap.com) para ofrecer su catálogo de estilos y
tema basado en [Bootstrap](https://getbootstrap.com) para integrar su catálogo de estilos y
componentes flexibles.

View file

@ -14,7 +14,7 @@ async fn hello_name(
) -> ResultPage<Markup, ErrorPage> {
let name = path.into_inner();
Page::new(request)
.add_component(Html::with(move |_| html! { h1 { "Hello " (name) "!" } }))
.add_child(Html::with(move |_| html! { h1 { "Hello " (name) "!" } }))
.render()
}

View file

@ -10,7 +10,7 @@ impl Extension for HelloWorld {
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.add_component(Html::with(move |_| html! { h1 { "Hello World!" } }))
.add_child(Html::with(|_| html! { h1 { "Hello World!" } }))
.render()
}

View file

@ -67,10 +67,10 @@ use pagetop::prelude::*;
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_theme("Aliner")
.add_component(
.add_child(
Block::new()
.with_title(L10n::l("sample_title"))
.add_component(Html::with(|cx| html! {
.add_child(Html::with(|cx| html! {
p { (L10n::l("sample_content").using(cx)) }
})),
)

View file

@ -68,10 +68,10 @@ use pagetop::prelude::*;
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_theme("Aliner")
.add_component(
.add_child(
Block::new()
.with_title(L10n::l("sample_title"))
.add_component(Html::with(|cx| html! {
.add_child(Html::with(|cx| html! {
p { (L10n::l("sample_content").using(cx)) }
})),
)

View file

@ -4,7 +4,7 @@ version = "0.0.18"
edition = "2021"
description = """
Tema de PageTop basado en Bootstrap para dar vida a tus diseños web.
Tema de PageTop basado en Bootstrap para aplicar su catálogo de estilos y componentes flexibles.
"""
categories = ["web-programming", "gui"]
keywords = ["pagetop", "theme", "bootstrap", "css", "js"]
@ -16,6 +16,7 @@ authors.workspace = true
[dependencies]
pagetop.workspace = true
serde.workspace = true
[build-dependencies]
pagetop-build.workspace = true

View file

@ -2,7 +2,7 @@
<h1>PageTop Bootsier</h1>
<p>Tema de <strong>PageTop</strong> basado en Bootstrap para dar vida a tus diseños web.</p>
<p>Tema de <strong>PageTop</strong> basado en Bootstrap para aplicar su catálogo de estilos y componentes flexibles.</p>
[![Doc API](https://img.shields.io/docsrs/pagetop-bootsier?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-bootsier)
[![Crates.io](https://img.shields.io/crates/v/pagetop-bootsier.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-bootsier)
@ -67,10 +67,10 @@ use pagetop::prelude::*;
async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_theme("Bootsier")
.add_component(
.add_child(
Block::new()
.with_title(L10n::l("sample_title"))
.add_component(Html::with(|cx| html! {
.add_child(Html::with(|cx| html! {
p { (L10n::l("sample_content").using(cx)) }
})),
)

View file

@ -3,7 +3,7 @@
<h1>PageTop Bootsier</h1>
<p>Tema de <strong>PageTop</strong> basado en Bootstrap para ofrecer su catálogo de estilos y componentes flexibles.</p>
<p>Tema de <strong>PageTop</strong> basado en Bootstrap para aplicar su catálogo de estilos y componentes flexibles.</p>
[![Doc API](https://img.shields.io/docsrs/pagetop-bootsier?label=Doc%20API&style=for-the-badge&logo=Docs.rs)](https://docs.rs/pagetop-bootsier)
[![Crates.io](https://img.shields.io/crates/v/pagetop-bootsier.svg?style=for-the-badge&logo=ipfs)](https://crates.io/crates/pagetop-bootsier)

View file

@ -71,7 +71,7 @@ impl Block {
}
/// Añade un nuevo componente hijo al bloque.
pub fn add_component(mut self, component: impl Component) -> Self {
pub fn add_child(mut self, component: impl Component) -> Self {
self.children
.alter_child(ChildOp::Add(Child::with(component)));
self

View file

@ -65,10 +65,10 @@ pub enum IntroOpening {
/// ```rust
/// # use pagetop::prelude::*;
/// let intro = Intro::default()
/// .add_component(
/// .add_child(
/// Block::new()
/// .with_title(L10n::l("intro_custom_block_title"))
/// .add_component(Html::with(move |cx| {
/// .add_child(Html::with(move |cx| {
/// html! {
/// p { (L10n::l("intro_custom_paragraph_1").using(cx)) }
/// p { (L10n::l("intro_custom_paragraph_2").using(cx)) }
@ -301,7 +301,7 @@ impl Intro {
/// Añade un nuevo componente hijo a la intro.
///
/// Si es un bloque ([`Block`]) aplica estilos específicos para destacarlo.
pub fn add_component(mut self, component: impl Component) -> Self {
pub fn add_child(mut self, component: impl Component) -> Self {
self.children
.alter_child(ChildOp::Add(Child::with(component)));
self

View file

@ -26,22 +26,22 @@ async fn homepage(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.with_title(L10n::l("welcome_title"))
.add_component(
.add_child(
Intro::new()
.add_component(
.add_child(
Block::new()
.with_title(L10n::l("welcome_status_title"))
.add_component(Html::with(move |cx| {
.add_child(Html::with(move |cx| {
html! {
p { (L10n::l("welcome_status_1").using(cx)) }
p { (L10n::l("welcome_status_2").using(cx)) }
}
})),
)
.add_component(
.add_child(
Block::new()
.with_title(L10n::l("welcome_support_title"))
.add_component(Html::with(move |cx| {
.add_child(Html::with(move |cx| {
html! {
p { (L10n::l("welcome_support_1").using(cx)) }
p { (L10n::l("welcome_support_2").with_arg("app", app).using(cx)) }

View file

@ -10,4 +10,9 @@ pub use children::{Typed, TypedOp};
mod context;
pub use context::{Context, ContextError, ContextOp, Contextual};
/// Alias de función (*callback*) para **resolver una URL** según el contexto de renderizado.
///
/// Se usa para generar enlaces dinámicos en función del contexto (petición, idioma, etc.). Debe
/// devolver una referencia válida durante el renderizado.
pub type FnPathByContext = fn(cx: &Context) -> &str;

View file

@ -145,9 +145,9 @@ impl UnitValue {
impl fmt::Display for UnitValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UnitValue::None => write!(f, ""),
UnitValue::Auto => write!(f, "auto"),
UnitValue::Zero => write!(f, "0"),
UnitValue::None => Ok(()),
UnitValue::Auto => f.write_str("auto"),
UnitValue::Zero => f.write_str("0"),
// Valor absoluto.
UnitValue::Cm(av) => write!(f, "{av}cm"),
UnitValue::In(av) => write!(f, "{av}in"),

View file

@ -61,7 +61,7 @@ impl Extension for HelloWorld {
async fn hello_world(request: HttpRequest) -> ResultPage<Markup, ErrorPage> {
Page::new(request)
.add_component(Html::with(move |_| html! { h1 { "Hello World!" } }))
.add_child(Html::with(|_| html! { h1 { "Hello World!" } }))
.render()
}

View file

@ -93,29 +93,28 @@ impl Page {
self
}
/// **Obsoleto desde la versión 0.4.0**: usar [`add_component()`](Self::add_component) en su
/// lugar.
#[deprecated(since = "0.4.0", note = "Use `add_component()` instead")]
/// **Obsoleto desde la versión 0.4.0**: usar [`add_child()`](Self::add_child) en su lugar.
#[deprecated(since = "0.4.0", note = "Use `add_child()` instead")]
pub fn with_component(self, component: impl Component) -> Self {
self.add_component(component)
self.add_child(component)
}
/// **Obsoleto desde la versión 0.4.0**: usar [`add_component_in()`](Self::add_component_in) en
/// su lugar.
#[deprecated(since = "0.4.0", note = "Use `add_component_in()` instead")]
/// **Obsoleto desde la versión 0.4.0**: usar [`add_child_in()`](Self::add_child_in) en su
/// lugar.
#[deprecated(since = "0.4.0", note = "Use `add_child_in()` instead")]
pub fn with_component_in(self, region_key: &'static str, component: impl Component) -> Self {
self.add_component_in(region_key, component)
self.add_child_in(region_key, component)
}
/// Añade un componente a la región de contenido por defecto.
pub fn add_component(mut self, component: impl Component) -> 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(REGION_CONTENT, ChildOp::Add(Child::with(component)));
self
}
/// Añade un componente en una región (`region_key`) de la página.
pub fn add_component_in(mut self, region_key: &'static str, component: impl Component) -> Self {
/// Añade un componente hijo en una región (`region_key`) de la página.
pub fn add_child_in(mut self, region_key: &'static str, component: impl Component) -> Self {
self.context
.alter_child_in(region_key, ChildOp::Add(Child::with(component)));
self

View file

@ -24,9 +24,9 @@ impl Display for ErrorPage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// Error 304.
ErrorPage::NotModified(_) => write!(f, "Not Modified"),
ErrorPage::NotModified(_) => f.write_str("Not Modified"),
// Error 400.
ErrorPage::BadRequest(_) => write!(f, "Bad Client Data"),
ErrorPage::BadRequest(_) => f.write_str("Bad Client Data"),
// Error 403.
ErrorPage::AccessDenied(request) => {
let mut error_page = Page::new(request.clone());
@ -34,12 +34,12 @@ impl Display for ErrorPage {
if let Ok(page) = error_page
.with_title(L10n::n("Error FORBIDDEN"))
.with_layout("error")
.add_component(Html::with(move |_| error403.clone()))
.add_child(Html::with(move |_| error403.clone()))
.render()
{
write!(f, "{}", page.into_string())
} else {
write!(f, "Access Denied")
f.write_str("Access Denied")
}
}
// Error 404.
@ -49,20 +49,20 @@ impl Display for ErrorPage {
if let Ok(page) = error_page
.with_title(L10n::n("Error RESOURCE NOT FOUND"))
.with_layout("error")
.add_component(Html::with(move |_| error404.clone()))
.add_child(Html::with(move |_| error404.clone()))
.render()
{
write!(f, "{}", page.into_string())
} else {
write!(f, "Not Found")
f.write_str("Not Found")
}
}
// Error 412.
ErrorPage::PreconditionFailed(_) => write!(f, "Precondition Failed"),
ErrorPage::PreconditionFailed(_) => f.write_str("Precondition Failed"),
// Error 500.
ErrorPage::InternalError(_) => write!(f, "Internal Error"),
ErrorPage::InternalError(_) => f.write_str("Internal Error"),
// Error 504.
ErrorPage::Timeout(_) => write!(f, "Timeout"),
ErrorPage::Timeout(_) => f.write_str("Timeout"),
}
}
}

View file

@ -70,14 +70,12 @@ macro_rules! join {
};
}
/// Concatena los fragmentos no vacíos en un [`Option<String>`] con un separador opcional.
/// Concatena los fragmentos **no vacíos** en un [`Option<String>`] con un separador opcional.
///
/// Esta macro acepta cualquier número de fragmentos que implementen [`AsRef<str>`] para concatenar
/// todos los fragmentos no vacíos usando opcionalmente un separador.
/// Acepta cualquier número de fragmentos que implementen [`AsRef<str>`]. Si todos los fragmentos
/// están vacíos, devuelve `None`.
///
/// Si todos los fragmentos están vacíos, devuelve [`None`].
///
/// # Ejemplo
/// # Ejemplos
///
/// ```rust
/// # use pagetop::prelude::*;
@ -90,7 +88,7 @@ macro_rules! join {
/// assert_eq!(result_without_separator, Some("HelloWorld".to_string()));
///
/// // Devuelve `None` si todos los fragmentos están vacíos.
/// let result_empty = join_opt!(["", "", ""]);
/// let result_empty = join_opt!(["", "", ""]; ",");
/// assert_eq!(result_empty, None);
/// ```
#[macro_export]
@ -100,12 +98,16 @@ macro_rules! join_opt {
(!s.is_empty()).then_some(s)
}};
([$($arg:expr),* $(,)?]; $separator:expr) => {{
let s = [$($arg),*]
.iter()
.filter(|&item| !item.is_empty())
.cloned()
.collect::<Vec<_>>()
.join($separator);
let sep = ($separator).as_ref();
let mut s = String::new();
for part in [ $( ($arg).as_ref() ),* ] {
if !(part as &str).is_empty() {
if !s.is_empty() {
s.push_str(sep);
}
s.push_str(part);
}
}
(!s.is_empty()).then_some(s)
}};
}
@ -153,7 +155,7 @@ macro_rules! join_pair {
}};
}
/// Concatena varios fragmentos en un [`Option<String>`] si ninguno está vacío.
/// Concatena varios fragmentos en un [`Option<String>`] **si ninguno está vacío**.
///
/// Si alguno de los fragmentos, que deben implementar [`AsRef<str>`], está vacío, devuelve
/// [`None`]. Opcionalmente se puede indicar un separador entre los fragmentos concatenados.