diff --git a/pagetop/src/base/action.rs b/pagetop/src/base/action.rs index aacd908a..35ce6b28 100644 --- a/pagetop/src/base/action.rs +++ b/pagetop/src/base/action.rs @@ -2,6 +2,8 @@ use crate::prelude::*; pub type FnActionWithComponent = fn(component: &mut C, cx: &mut Context); +pub type FnActionWithPage = fn(page: &mut Page); + pub mod component; pub mod layout; diff --git a/pagetop/src/base/action/layout.rs b/pagetop/src/base/action/layout.rs index 495b2a1f..a8f620a7 100644 --- a/pagetop/src/base/action/layout.rs +++ b/pagetop/src/base/action/layout.rs @@ -6,3 +6,9 @@ pub use after_render_component::*; mod render_component; pub use render_component::*; + +mod before_render_page_body; +pub use before_render_page_body::*; + +mod after_render_page_body; +pub use after_render_page_body::*; diff --git a/pagetop/src/base/action/layout/after_render_page_body.rs b/pagetop/src/base/action/layout/after_render_page_body.rs new file mode 100644 index 00000000..21b6dd6f --- /dev/null +++ b/pagetop/src/base/action/layout/after_render_page_body.rs @@ -0,0 +1,37 @@ +use crate::prelude::*; + +use crate::base::action::FnActionWithPage; + +pub struct AfterRenderBody { + f: FnActionWithPage, + layout_type_id: Option, +} + +impl ActionTrait for AfterRenderBody { + fn layout_type_id(&self) -> Option { + self.layout_type_id + } +} + +impl AfterRenderBody { + pub fn new(layout: LayoutRef, f: FnActionWithPage) -> Self { + AfterRenderBody { + f, + layout_type_id: Some(layout.type_id()), + } + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(page: &mut Page) { + dispatch_actions( + &ActionKey::new( + TypeId::of::(), + Some(page.context().layout().type_id()), + None, + None, + ), + |action: &Self| (action.f)(page), + ); + } +} diff --git a/pagetop/src/base/action/layout/before_render_page_body.rs b/pagetop/src/base/action/layout/before_render_page_body.rs new file mode 100644 index 00000000..08a1e1e8 --- /dev/null +++ b/pagetop/src/base/action/layout/before_render_page_body.rs @@ -0,0 +1,37 @@ +use crate::prelude::*; + +use crate::base::action::FnActionWithPage; + +pub struct BeforeRenderBody { + f: FnActionWithPage, + layout_type_id: Option, +} + +impl ActionTrait for BeforeRenderBody { + fn layout_type_id(&self) -> Option { + self.layout_type_id + } +} + +impl BeforeRenderBody { + pub fn new(layout: LayoutRef, f: FnActionWithPage) -> Self { + BeforeRenderBody { + f, + layout_type_id: Some(layout.type_id()), + } + } + + #[inline(always)] + #[allow(clippy::inline_always)] + pub(crate) fn dispatch(page: &mut Page) { + dispatch_actions( + &ActionKey::new( + TypeId::of::(), + Some(page.context().layout().type_id()), + None, + None, + ), + |action: &Self| (action.f)(page), + ); + } +} diff --git a/pagetop/src/base/action/page/after_render_body.rs b/pagetop/src/base/action/page/after_render_body.rs index 81a89d87..f76a8d56 100644 --- a/pagetop/src/base/action/page/after_render_body.rs +++ b/pagetop/src/base/action/page/after_render_body.rs @@ -1,9 +1,9 @@ use crate::prelude::*; -pub type FnAfterRenderBody = fn(page: &mut Page); +use crate::base::action::FnActionWithPage; pub struct AfterRenderBody { - f: FnAfterRenderBody, + f: FnActionWithPage, weight: Weight, } @@ -14,7 +14,7 @@ impl ActionTrait for AfterRenderBody { } impl AfterRenderBody { - pub fn new(f: FnAfterRenderBody) -> Self { + pub fn new(f: FnActionWithPage) -> Self { AfterRenderBody { f, weight: 0 } } diff --git a/pagetop/src/base/action/page/before_render_body.rs b/pagetop/src/base/action/page/before_render_body.rs index e0a9d770..45842269 100644 --- a/pagetop/src/base/action/page/before_render_body.rs +++ b/pagetop/src/base/action/page/before_render_body.rs @@ -1,9 +1,9 @@ use crate::prelude::*; -pub type FnBeforeRenderBody = fn(page: &mut Page); +use crate::base::action::FnActionWithPage; pub struct BeforeRenderBody { - f: FnBeforeRenderBody, + f: FnActionWithPage, weight: Weight, } @@ -14,7 +14,7 @@ impl ActionTrait for BeforeRenderBody { } impl BeforeRenderBody { - pub fn new(f: FnBeforeRenderBody) -> Self { + pub fn new(f: FnActionWithPage) -> Self { BeforeRenderBody { f, weight: 0 } } diff --git a/pagetop/src/base/layout.rs b/pagetop/src/base/layout.rs index 42ba2b48..3a192746 100644 --- a/pagetop/src/base/layout.rs +++ b/pagetop/src/base/layout.rs @@ -1,11 +1,2 @@ -use crate::prelude::*; - -pub struct Basic; - -impl PackageTrait for Basic { - fn layout(&self) -> Option { - Some(&Basic) - } -} - -impl LayoutTrait for Basic {} +mod basic; +pub use basic::Basic; diff --git a/pagetop/src/base/layout/basic.rs b/pagetop/src/base/layout/basic.rs new file mode 100644 index 00000000..42ba2b48 --- /dev/null +++ b/pagetop/src/base/layout/basic.rs @@ -0,0 +1,11 @@ +use crate::prelude::*; + +pub struct Basic; + +impl PackageTrait for Basic { + fn layout(&self) -> Option { + Some(&Basic) + } +} + +impl LayoutTrait for Basic {} diff --git a/pagetop/src/base/package.rs b/pagetop/src/base/package.rs index 37becfd8..1b0fa82b 100644 --- a/pagetop/src/base/package.rs +++ b/pagetop/src/base/package.rs @@ -1,133 +1,2 @@ -use crate::prelude::*; - -pub struct Welcome; - -impl PackageTrait for Welcome { - fn name(&self) -> L10n { - L10n::l("welcome_package_name") - } - - fn description(&self) -> L10n { - L10n::l("welcome_package_description") - } - - fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { - scfg.route("/", service::web::get().to(homepage)); - } -} - -async fn homepage(request: HttpRequest) -> ResultPage { - Page::new(request) - .with_title(L10n::l("welcome_page")) - .with_assets(AssetsOp::Layout("Basic")) - .with_assets(AssetsOp::AddStyleSheet(StyleSheet::inline("styles", r##" - body { - background-color: #f3d060; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - font-size: 20px; - } - .skip__to_content { - display: none; - } - .wrapper { - max-width: 1200px; - width: 100%; - margin: 0 auto; - padding: 0; - } - .container { - padding: 0 16px; - } - .title { - font-size: clamp(3rem, 10vw, 10rem); - letter-spacing: -0.05em; - line-height: 1.2; - margin: 0; - } - .subtitle { - font-size: clamp(1.8rem, 2vw, 3rem); - letter-spacing: -0.02em; - line-height: 1.2; - margin: 0; - } - .powered { - margin: .5em 0 1em; - } - .box-container { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - align-items: stretch; - gap: 1.5em; - } - .box { - flex: 1 1 280px; - border: 3px solid #25282a; - box-shadow: 5px 5px 0px #25282a; - box-sizing: border-box; - padding: 0 16px; - } - footer { - margin-top: 5em; - font-size: 14px; - font-weight: 500; - color: #a5282c; - } - "##))) - .with_component(Html::with(html! { - div class="wrapper" { - div class="container" { - h1 class="title" { (L10n::l("welcome_title").markup()) } - - p class="subtitle" { - (L10n::l("welcome_intro").with_arg("app", format!( - "{}", - &global::SETTINGS.app.name - )).markup()) - } - p class="powered" { - (L10n::l("welcome_powered").with_arg("pagetop", format!( - "{}", - "https://crates.io/crates/pagetop", "PageTop" - )).markup()) - } - - h2 { (L10n::l("welcome_page").markup()) } - - div class="box-container" { - section class="box" style="background-color: #5eb0e5;" { - h3 { - (L10n::l("welcome_subtitle") - .with_arg("app", &global::SETTINGS.app.name) - .markup()) - } - p { (L10n::l("welcome_text1").markup()) } - p { (L10n::l("welcome_text2").markup()) } - } - section class="box" style="background-color: #aee1cd;" { - h3 { - (L10n::l("welcome_pagetop_title").markup()) - } - p { (L10n::l("welcome_pagetop_text1").markup()) } - p { (L10n::l("welcome_pagetop_text2").markup()) } - p { (L10n::l("welcome_pagetop_text3").markup()) } - } - section class="box" style="background-color: #ebebe3;" { - h3 { - (L10n::l("welcome_issues_title").markup()) - } - p { (L10n::l("welcome_issues_text1").markup()) } - p { - (L10n::l("welcome_issues_text2") - .with_arg("app", &global::SETTINGS.app.name) - .markup()) - } - } - } - - footer { "[ " (L10n::l("welcome_have_fun").markup()) " ]" } - } - } - })) - .render() -} +mod welcome; +pub use welcome::Welcome; diff --git a/pagetop/src/base/package/welcome.rs b/pagetop/src/base/package/welcome.rs new file mode 100644 index 00000000..37becfd8 --- /dev/null +++ b/pagetop/src/base/package/welcome.rs @@ -0,0 +1,133 @@ +use crate::prelude::*; + +pub struct Welcome; + +impl PackageTrait for Welcome { + fn name(&self) -> L10n { + L10n::l("welcome_package_name") + } + + fn description(&self) -> L10n { + L10n::l("welcome_package_description") + } + + fn configure_service(&self, scfg: &mut service::web::ServiceConfig) { + scfg.route("/", service::web::get().to(homepage)); + } +} + +async fn homepage(request: HttpRequest) -> ResultPage { + Page::new(request) + .with_title(L10n::l("welcome_page")) + .with_assets(AssetsOp::Layout("Basic")) + .with_assets(AssetsOp::AddStyleSheet(StyleSheet::inline("styles", r##" + body { + background-color: #f3d060; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-size: 20px; + } + .skip__to_content { + display: none; + } + .wrapper { + max-width: 1200px; + width: 100%; + margin: 0 auto; + padding: 0; + } + .container { + padding: 0 16px; + } + .title { + font-size: clamp(3rem, 10vw, 10rem); + letter-spacing: -0.05em; + line-height: 1.2; + margin: 0; + } + .subtitle { + font-size: clamp(1.8rem, 2vw, 3rem); + letter-spacing: -0.02em; + line-height: 1.2; + margin: 0; + } + .powered { + margin: .5em 0 1em; + } + .box-container { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: stretch; + gap: 1.5em; + } + .box { + flex: 1 1 280px; + border: 3px solid #25282a; + box-shadow: 5px 5px 0px #25282a; + box-sizing: border-box; + padding: 0 16px; + } + footer { + margin-top: 5em; + font-size: 14px; + font-weight: 500; + color: #a5282c; + } + "##))) + .with_component(Html::with(html! { + div class="wrapper" { + div class="container" { + h1 class="title" { (L10n::l("welcome_title").markup()) } + + p class="subtitle" { + (L10n::l("welcome_intro").with_arg("app", format!( + "{}", + &global::SETTINGS.app.name + )).markup()) + } + p class="powered" { + (L10n::l("welcome_powered").with_arg("pagetop", format!( + "{}", + "https://crates.io/crates/pagetop", "PageTop" + )).markup()) + } + + h2 { (L10n::l("welcome_page").markup()) } + + div class="box-container" { + section class="box" style="background-color: #5eb0e5;" { + h3 { + (L10n::l("welcome_subtitle") + .with_arg("app", &global::SETTINGS.app.name) + .markup()) + } + p { (L10n::l("welcome_text1").markup()) } + p { (L10n::l("welcome_text2").markup()) } + } + section class="box" style="background-color: #aee1cd;" { + h3 { + (L10n::l("welcome_pagetop_title").markup()) + } + p { (L10n::l("welcome_pagetop_text1").markup()) } + p { (L10n::l("welcome_pagetop_text2").markup()) } + p { (L10n::l("welcome_pagetop_text3").markup()) } + } + section class="box" style="background-color: #ebebe3;" { + h3 { + (L10n::l("welcome_issues_title").markup()) + } + p { (L10n::l("welcome_issues_text1").markup()) } + p { + (L10n::l("welcome_issues_text2") + .with_arg("app", &global::SETTINGS.app.name) + .markup()) + } + } + } + + footer { "[ " (L10n::l("welcome_have_fun").markup()) " ]" } + } + } + })) + .render() +} diff --git a/pagetop/src/core/component/definition.rs b/pagetop/src/core/component/definition.rs index a58d6958..0321daac 100644 --- a/pagetop/src/core/component/definition.rs +++ b/pagetop/src/core/component/definition.rs @@ -40,7 +40,7 @@ impl ComponentBase for C { // Comprueba el componente antes de prepararlo. self.setup_before_prepare(cx); - // Acciones del diseño antes de renderizar el componente. + // Acciones específicas del diseño antes de renderizar el componente. action::layout::BeforeRender::dispatch(self, cx); // Acciones de los paquetes antes de renderizar el componente. @@ -52,7 +52,7 @@ impl ComponentBase for C { None => self.prepare_component(cx).render(), }; - // Acciones del diseño después de renderizar el componente. + // Acciones específicas del diseño después de renderizar el componente. action::layout::AfterRender::dispatch(self, cx); // Acciones de los paquetes después de renderizar el componente. diff --git a/pagetop/src/core/layout/definition.rs b/pagetop/src/core/layout/definition.rs index c34891e0..3ab4e976 100644 --- a/pagetop/src/core/layout/definition.rs +++ b/pagetop/src/core/layout/definition.rs @@ -43,9 +43,6 @@ pub trait LayoutTrait: PackageTrait + Send + Sync { } } - #[allow(unused_variables)] - fn before_render_body(&self, page: &mut Page) {} - fn render_body(&self, page: &mut Page) -> Markup { html! { body id=[page.body_id().get()] class=[page.body_classes().get()] { @@ -53,7 +50,4 @@ pub trait LayoutTrait: PackageTrait + Send + Sync { } } } - - #[allow(unused_variables)] - fn after_render_body(&self, page: &mut Page) {} } diff --git a/pagetop/src/response/page.rs b/pagetop/src/response/page.rs index ede1855a..d099af2d 100644 --- a/pagetop/src/response/page.rs +++ b/pagetop/src/response/page.rs @@ -156,25 +156,25 @@ impl Page { // Page RENDER. pub fn render(&mut self) -> ResultPage { - // Layout-specific operations before rendering the page body. - self.context.layout().before_render_body(self); + // Acciones específicas del diseño antes de renderizar el . + action::layout::BeforeRenderBody::dispatch(self); - // Execute package actions before rendering the page body. + // Acciones de los paquetes antes de renderizar el . action::page::BeforeRenderBody::dispatch(self); - // Render the page body. + // Renderiza el . let body = self.context.layout().render_body(self); - // Layout-specific operations after rendering the page body. - self.context.layout().after_render_body(self); + // Acciones específicas del diseño después de renderizar el . + action::layout::AfterRenderBody::dispatch(self); - // Execute package actions after rendering the page body. + // Acciones de los paquetes después de renderizar el . action::page::AfterRenderBody::dispatch(self); - // Render the page head. + // Renderiza el . let head = self.context.layout().render_head(self); - // Render the full page with language and direction attributes. + // Compone la página completa incluyendo los atributos de idioma y dirección del texto. let lang = &self.context.langid().language; let dir = match self.context.langid().character_direction() { CharacterDirection::LTR => "ltr",