diff --git a/src/base/extension/welcome.rs b/src/base/extension/welcome.rs index 3c0de3d..eaa9f33 100644 --- a/src/base/extension/welcome.rs +++ b/src/base/extension/welcome.rs @@ -24,11 +24,13 @@ impl Extension for Welcome { async fn homepage(request: HttpRequest) -> ResultPage { let app = &global::SETTINGS.app.name; - Page::new(Some(request)) + Page::new(request) .with_theme("basic") .with_layout("intro") .with_title(L10n::l("welcome_title")) .with_description(L10n::l("welcome_intro").with_arg("app", app)) + .with_param("intro_button_text", L10n::l("welcome_powered")) + .with_param("intro_button_link", "https://pagetop.cillero.es".to_owned()) .add_component(Html::with(|cx| { html! { p { (L10n::l("welcome_text1").using(cx)) } diff --git a/src/base/theme/basic.rs b/src/base/theme/basic.rs index dc16f2a..fbf4caf 100644 --- a/src/base/theme/basic.rs +++ b/src/base/theme/basic.rs @@ -41,6 +41,9 @@ fn render_intro(page: &mut Page) -> Markup { let title = page.title().unwrap_or_default(); let intro = page.description().unwrap_or_default(); + let intro_button_text: L10n = page.param_or_default("intro_button_text"); + let intro_button_link: Option<&String> = page.param("intro_button_link"); + html! { body id=[page.body_id().get()] class=[page.body_classes().get()] { header class="intro-header" { @@ -71,16 +74,18 @@ fn render_intro(page: &mut Page) -> Markup { } main class="intro-content" { section class="intro-content__body" { - div class="intro-button" { - a - class="intro-button__link" - href="https://pagetop.cillero.es" - target="_blank" - rel="noreferrer" - { - span {} span {} span {} - div class="intro-button__text" { - (L10n::l("welcome_powered").using(page)) + @if intro_button_link.is_some() { + div class="intro-button" { + a + class="intro-button__link" + href=[intro_button_link] + target="_blank" + rel="noreferrer" + { + span {} span {} span {} + div class="intro-button__text" { + (intro_button_text.using(page)) + } } } } diff --git a/src/html/context.rs b/src/html/context.rs index 79148b0..26e2478 100644 --- a/src/html/context.rs +++ b/src/html/context.rs @@ -118,6 +118,21 @@ pub trait Contextual: LangId { /// Recupera un parámetro como [`Option`]. fn param(&self, key: &'static str) -> Option<&T>; + /// Devuelve el parámetro clonado o el **valor por defecto del tipo** (`T::default()`). + fn param_or_default(&self, key: &'static str) -> T { + self.param::(key).cloned().unwrap_or_default() + } + + /// Devuelve el parámetro clonado o un **valor por defecto** si no existe. + fn param_or(&self, key: &'static str, default: T) -> T { + self.param::(key).cloned().unwrap_or(default) + } + + /// Devuelve el parámetro clonado o el **valor evaluado** por la función `f` si no existe. + fn param_or_else T>(&self, key: &'static str, f: F) -> T { + self.param::(key).cloned().unwrap_or_else(f) + } + /// Devuelve el Favicon de los recursos del contexto. fn favicon(&self) -> Option<&Favicon>; diff --git a/src/response/page.rs b/src/response/page.rs index 77bc9c4..86a0bdc 100644 --- a/src/response/page.rs +++ b/src/response/page.rs @@ -4,7 +4,6 @@ pub use error::ErrorPage; pub use actix_web::Result as ResultPage; use crate::base::action; -use crate::builder_fn; use crate::core::component::{Child, ChildOp, Component}; use crate::core::theme::{ChildrenInRegions, ThemeRef, REGION_CONTENT}; use crate::html::{html, Markup, DOCTYPE}; @@ -14,6 +13,7 @@ use crate::html::{AttrClasses, ClassesOp}; use crate::html::{AttrId, AttrL10n}; use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier}; use crate::service::HttpRequest; +use crate::{builder_fn, AutoDefault}; /// Representa una página HTML completa lista para renderizar. /// @@ -21,6 +21,7 @@ use crate::service::HttpRequest; /// regiones donde disponer los componentes, atributos de `` y otros aspectos del contexto de /// renderizado. #[rustfmt::skip] +#[derive(AutoDefault)] pub struct Page { title : AttrL10n, description : AttrL10n, @@ -35,10 +36,10 @@ pub struct Page { impl Page { /// Crea una nueva instancia de página. /// - /// Si se proporciona la solicitud HTTP, se guardará en el contexto de renderizado de la página - /// para poder ser recuperada por los componentes si es necesario. + /// La solicitud HTTP se guardará en el contexto de renderizado de la página para poder ser + /// recuperada por los componentes si es necesario. #[rustfmt::skip] - pub fn new(request: Option) -> Self { + pub fn new(request: HttpRequest) -> Self { Page { title : AttrL10n::default(), description : AttrL10n::default(), @@ -46,7 +47,7 @@ impl Page { properties : Vec::default(), body_id : AttrId::default(), body_classes: AttrClasses::default(), - context : Context::new(request), + context : Context::new(Some(request)), regions : ChildrenInRegions::default(), } } diff --git a/src/response/page/error.rs b/src/response/page/error.rs index be48e3e..2355d23 100644 --- a/src/response/page/error.rs +++ b/src/response/page/error.rs @@ -29,7 +29,7 @@ impl Display for ErrorPage { ErrorPage::BadRequest(_) => write!(f, "Bad Client Data"), // Error 403. ErrorPage::AccessDenied(request) => { - let mut error_page = Page::new(Some(request.clone())); + let mut error_page = Page::new(request.clone()); let error403 = error_page.theme().error403(&mut error_page); if let Ok(page) = error_page .with_title(L10n::n("Error FORBIDDEN")) @@ -44,7 +44,7 @@ impl Display for ErrorPage { } // Error 404. ErrorPage::NotFound(request) => { - let mut error_page = Page::new(Some(request.clone())); + let mut error_page = Page::new(request.clone()); let error404 = error_page.theme().error404(&mut error_page); if let Ok(page) = error_page .with_title(L10n::n("Error RESOURCE NOT FOUND")) diff --git a/static/css/intro.css b/static/css/intro.css index 5a5461e..19fa9f1 100644 --- a/static/css/intro.css +++ b/static/css/intro.css @@ -5,9 +5,13 @@ --bg-img-sm-set: image-set(url('/img/intro-header-sm.avif') type('image/avif'), url('/img/intro-header-sm.webp') type('image/webp'), var(--bg-img-sm) type('image/jpeg')); --bg-color: #8c5919; --color: #1a202c; - --color-red: #fecaca; --color-gray: #e4e4e7; --color-link: #1e4eae; + --color-block-1: #fecaca; + --color-block-2: #e6a9e2; + --color-block-3: #b689ff; + --color-block-4: #ffedca; + --color-block-5: #ffffff; --focus-outline: 2px solid var(--color-link); --focus-outline-offset: 2px; --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); @@ -413,9 +417,24 @@ a:hover:visited { left: -15%; width: 130%; z-index: -10; - background: var(--color-red); + background: var(--color-block-1); transform: rotate(2deg); } +.intro-text .block:nth-of-type(5n+1) .block__title:after { + background: var(--color-block-1); +} +.intro-text .block:nth-of-type(5n+2) .block__title:after { + background: var(--color-block-2); +} +.intro-text .block:nth-of-type(5n+3) .block__title:after { + background: var(--color-block-3); +} +.intro-text .block:nth-of-type(5n+4) .block__title:after { + background: var(--color-block-4); +} +.intro-text .block:nth-of-type(5n+5) .block__title:after { + background: var(--color-block-5); +} /* * Footer