diff --git a/examples/navbar-menus.rs b/examples/navbar-menus.rs index fcc6e84c..5c9ab44f 100644 --- a/examples/navbar-menus.rs +++ b/examples/navbar-menus.rs @@ -102,7 +102,7 @@ impl Extension for SuperMenu { )), )); - InRegion::Global(&CoreRegion::Header).add( + InRegion::Global(&CoreRegions::Header).add( bs::Container::new() .with_width(bs::container::Width::FluidMax(UnitValue::RelRem(75.0))) .with_child(navbar_menu), diff --git a/src/base/component/layout/region.rs b/src/base/component/layout/region.rs index 72c6cc96..cd9d4c53 100644 --- a/src/base/component/layout/region.rs +++ b/src/base/component/layout/region.rs @@ -55,7 +55,7 @@ impl fmt::Debug for Region { impl Default for Region { fn default() -> Self { Region { - region: &CoreRegion::Content, + region: &CoreRegions::Content, } } } @@ -90,17 +90,33 @@ impl Component for Region { } impl Region { - /// Define el componente que renderizará [`CoreRegion::Header`]. + /// Define el componente que renderizará [`CoreRegions::Header`]. pub fn header() -> Self { Region { - region: &CoreRegion::Header, + region: &CoreRegions::Header, } } - /// Define el componente que renderizará [`CoreRegion::Footer`]. + /// Define el componente que renderizará [`CoreRegions::Aside`]. + pub fn aside() -> Self { + Region { + region: &CoreRegions::Aside, + } + } + + /// Define el componente que renderizará [`CoreRegions::Content`]. + /// + /// Equivale a [`Self::new()`] o [`Self::default()`], que ya usan esta región por defecto. + pub fn content() -> Self { + Region { + region: &CoreRegions::Content, + } + } + + /// Define el componente que renderizará [`CoreRegions::Footer`]. pub fn footer() -> Self { Region { - region: &CoreRegion::Footer, + region: &CoreRegions::Footer, } } diff --git a/src/base/component/layout/template.rs b/src/base/component/layout/template.rs index 54fd3f64..66c21beb 100644 --- a/src/base/component/layout/template.rs +++ b/src/base/component/layout/template.rs @@ -4,25 +4,32 @@ use std::fmt; /// Componente que renderiza el cuerpo de una plantilla de regiones. /// -/// La composición por defecto usa el componente [`Region`](crate::base::component::layout::Region) -/// para mostrar, en este orden, las regiones [`CoreRegion::Header`], [`CoreRegion::Content`] y -/// [`CoreRegion::Footer`]. +/// La composición por defecto usa el componente [`Region`] para mostrar, en este orden, las +/// regiones [`CoreRegions::Header`], [`CoreRegions::Aside`], [`CoreRegions::Content`] y +/// [`CoreRegions::Footer`], envueltas en un contenedor `div.wrapper` que un tema puede maquetar a +/// su gusto (por ejemplo, usando CSS Grid, para que `Aside` se muestre como columna lateral junto a +/// `Content`). Si `Aside`, o cualquier otra región, no tiene contenido, no se renderiza. /// -/// No incluye las regiones reservadas -/// [`ReservedRegion::PageTop`](crate::response::ReservedRegion::PageTop) y -/// [`ReservedRegion::PageBottom`](crate::response::ReservedRegion::PageBottom) porque el propio -/// [`Page::render()`](crate::response::Page::render) las añade antes y después del resultado de -/// [`Theme::render_page_body()`](crate::core::theme::Theme::render_page_body) para que se -/// rendericen siempre, independientemente de la plantilla que se use. +/// No incluye las regiones reservadas ([`ReservedRegions::PageTop`] y +/// [`ReservedRegions::PageBottom`]) porque el propio [`Page::render()`] las añade antes y después +/// del resultado de [`Theme::render_page_body()`] para que se rendericen siempre, +/// independientemente de la plantilla que se use. /// /// Si un tema necesita maquetar una plantilla determinada de forma distinta, puede capturar este -/// componente en [`Theme::handle_component()`](crate::core::theme::Theme::handle_component) y hacer -/// [`downcast_ref()`](crate::core::AnyCast::downcast_ref) sobre el [`TemplateRef`] que devuelve -/// [`Self::template()`], para compararlo con la variante deseada. +/// componente en [`Theme::handle_component()`] y hacer [`downcast_ref()`] sobre el [`TemplateRef`] +/// que devuelve [`Self::template()`], para compararlo con la variante deseada. /// /// Como cualquier otro componente, participa también en el despacho de las /// [acciones de componentes](crate::base::action::component) para que otras extensiones puedan /// intervenir en su renderizado. +/// +/// [`Region`]: crate::base::component::layout::Region +/// [`ReservedRegions::PageTop`]: crate::response::ReservedRegions::PageTop +/// [`ReservedRegions::PageBottom`]: crate::response::ReservedRegions::PageBottom +/// [`Page::render()`]: crate::response::Page::render +/// [`Theme::render_page_body()`]: crate::core::theme::Theme::render_page_body +/// [`Theme::handle_component()`]: crate::core::theme::Theme::handle_component +/// [`downcast_ref()`]: crate::core::AnyCast::downcast_ref #[derive(Clone, Getters)] pub struct Template { /// Devuelve la plantilla subyacente. @@ -41,7 +48,7 @@ impl fmt::Debug for Template { impl Default for Template { fn default() -> Self { Template { - template: &CoreTemplate::Standard, + template: &CoreTemplates::Standard, } } } @@ -58,19 +65,39 @@ impl Component for Template { } async fn prepare(&self, cx: &mut Context) -> Result { - Ok(html! { + let body = html! { (layout::Region::header().render(cx).await) - (layout::Region::default().render(cx).await) + (layout::Region::aside().render(cx).await) + (layout::Region::content().render(cx).await) (layout::Region::footer().render(cx).await) + }; + + if body.is_empty() { + return Ok(html! {}); + } + + Ok(html! { + div.wrapper { + (body) + } }) } } impl Template { - /// Define el componente que renderizará [`CoreTemplate::Admin`]. + /// Define el componente que renderizará [`CoreTemplates::Standard`]. + /// + /// Equivale a [`Self::new()`] o [`Self::default()`], que ya usan esta plantilla por defecto. + pub fn standard() -> Self { + Template { + template: &CoreTemplates::Standard, + } + } + + /// Define el componente que renderizará [`CoreTemplates::Admin`]. pub fn admin() -> Self { Template { - template: &CoreTemplate::Admin, + template: &CoreTemplates::Admin, } } diff --git a/src/base/theme/basic.rs b/src/base/theme/basic.rs index a1929526..48de920b 100644 --- a/src/base/theme/basic.rs +++ b/src/base/theme/basic.rs @@ -25,7 +25,7 @@ impl Theme for Basic { .with_weight(-99), )) .alter_child_in( - &CoreRegion::Footer, + &CoreRegions::Footer, ChildOp::AddIfEmpty(PoweredBy::new().into()), ); } diff --git a/src/core/component/context.rs b/src/core/component/context.rs index 6b1af5d2..6a9cfc03 100644 --- a/src/core/component/context.rs +++ b/src/core/component/context.rs @@ -2,7 +2,7 @@ use crate::auth::CurrentUser; use crate::core::TypeInfo; use crate::core::component::{ChildOp, Component, MessageLevel, StatusMessage}; use crate::core::theme::all::DEFAULT_THEME; -use crate::core::theme::{ChildrenInRegions, CoreRegion, CoreTemplate}; +use crate::core::theme::{ChildrenInRegions, CoreRegions, CoreTemplates}; use crate::core::theme::{RegionRef, TemplateRef, ThemeRef}; use crate::html::{Assets, Favicon, JavaScript, Preload, StyleSheet}; use crate::html::{Markup, Props, PropsOp, RoutePath, html}; @@ -77,7 +77,7 @@ pub enum ContextError { /// # use pagetop_aliner::Aliner; /// fn prepare_context(cx: C) -> C { /// cx.with_langid(&Locale::resolve("es-ES")) -/// .with_template(&CoreTemplate::Standard) +/// .with_template(&CoreTemplates::Standard) /// .with_theme(&Aliner) /// .with_assets(AssetsOp::SetFavicon(Some(Favicon::new().with_icon("/favicon.ico")))) /// .with_assets(AssetsOp::AddStyleSheet(StyleSheet::from("/css/app.css"))) @@ -330,7 +330,7 @@ pub struct Context { impl Default for Context { fn default() -> Self { - Self::base(None, &CoreTemplate::Standard) + Self::base(None, &CoreTemplates::Standard) } } @@ -369,15 +369,15 @@ impl Context { /// Para un contexto sin petición (renderizar un componente de forma aislada, en tests o fuera /// del ciclo de una petición web), usa [`Context::default()`]. pub fn new(request: HttpRequest) -> Self { - Self::base(Some(request), &CoreTemplate::Standard) + Self::base(Some(request), &CoreTemplates::Standard) } /// Crea un nuevo contexto asociado a una petición HTTP, con la plantilla de administración. /// - /// El contexto inicializa el idioma, el tema y la plantilla [`CoreTemplate::Admin`], sin + /// El contexto inicializa el idioma, el tema y la plantilla [`CoreTemplates::Admin`], sin /// favicon ni otros recursos cargados. pub fn admin(request: HttpRequest) -> Self { - Self::base(Some(request), &CoreTemplate::Admin) + Self::base(Some(request), &CoreTemplates::Admin) } // Extrae el `CurrentUser` inyectado por middleware en las extensiones de la petición, o @@ -629,7 +629,8 @@ impl Contextual for Context { #[builder_fn] fn with_child(mut self, op: impl Into) -> Self { - self.regions.alter_child_in(&CoreRegion::Content, op.into()); + self.regions + .alter_child_in(&CoreRegions::Content, op.into()); self } diff --git a/src/core/theme.rs b/src/core/theme.rs index f89746d9..bdc3ac81 100644 --- a/src/core/theme.rs +++ b/src/core/theme.rs @@ -44,15 +44,14 @@ //! Un tema puede personalizarse en tres pasos, cada uno necesario sólo si lo que ofrece PageTop por //! defecto no basta: //! -//! 1. **Definir regiones nuevas**. Por defecto, PageTop define [`CoreRegion`] (`Header`, `Content`, -//! `Footer`) como las regiones de plantilla que se asumen siempre disponibles, y -//! [`ReservedRegion`](crate::response::ReservedRegion) (`PageTop`, `PageBottom`) como las -//! regiones reservadas que se renderizan al margen de cualquier plantilla. Un tema puede definir -//! su propio *enum* que implemente [`RegionName`] para **añadir** nuevas regiones que PageTop no -//! ofrece (por ejemplo, una barra lateral). No es necesario redefinir las de [`CoreRegion`] ni -//! las de [`ReservedRegion`](crate::response::ReservedRegion), que ya existen y se asume que -//! cualquier tema respeta. -//! 2. **Definir plantillas nuevas**. Por defecto existe [`CoreTemplate`], con las plantillas +//! 1. **Definir regiones nuevas**. Por defecto, PageTop define [`CoreRegions`] (`Header`, `Aside`, +//! `Content`, `Footer`) como regiones de plantilla siempre disponibles, y [`ReservedRegions`] +//! (`PageTop`, `PageBottom`) como regiones reservadas que se renderizan al margen de cualquier +//! plantilla. Un tema puede definir su propio *enum* que implemente [`RegionName`] para +//! **añadir** nuevas regiones que PageTop no ofrece (como barras laterales, regiones específicas +//! para menús, sliders, cabeceras hero, etc.). No es necesario redefinir las de [`CoreRegions`] +//! ni las de [`ReservedRegions`], que ya existen y se asume que cualquier tema respeta. +//! 2. **Definir plantillas nuevas**. Por defecto existe [`CoreTemplates`], con las plantillas //! `Standard` y `Admin` que usan `Page::new()` y `Page::admin()` respectivamente, y que son //! siempre las mismas: no hay un método de `Theme` para elegir una plantilla predeterminada //! distinta. Un tema puede definir su propio *enum* que implemente [`TemplateName`] para @@ -66,7 +65,7 @@ //! ([`Region::region()`](crate::base::component::layout::Region::region) o //! [`Template::template()`](crate::base::component::layout::Template::template)) con //! [`AnyCast::downcast_ref()`](crate::core::AnyCast::downcast_ref) hacia el tipo concreto (por -//! ejemplo, [`CoreTemplate`] o el propio *enum* del tema). `pagetop-bootsier` hace exactamente +//! ejemplo, [`CoreTemplates`] o el propio *enum* del tema). `pagetop-bootsier` hace exactamente //! esto para maquetar `Standard` y `Admin` de forma distinta, sin necesitar sus propias //! variantes de plantilla. //! @@ -96,7 +95,7 @@ //! //! ```rust,no_run //! # use pagetop::prelude::*; -//! InRegion::Global(&CoreRegion::Footer).add(PoweredBy::new()); +//! InRegion::Global(&CoreRegions::Footer).add(PoweredBy::new()); //! ``` //! //! El componente se guarda como **prototipo**: cada página recibe un clon fresco en el momento del @@ -108,6 +107,8 @@ //! [ciclo de renderizado](crate::core::component::ComponentRender). Esto permite registrarlo una //! sola vez y que decida por sí mismo cuándo mostrarse, por ejemplo según la ruta de la petición o //! si el usuario actual está autenticado. +//! +//! [`ReservedRegions`]: crate::response::ReservedRegions use crate::AutoDefault; use crate::core::AnyInfo; @@ -117,26 +118,30 @@ use crate::locale::L10n; /// Interfaz común para las regiones lógicas del ``. /// -/// Una `RegionName` representa un contenedor lógico identificado por un nombre de región. Su -/// contenido se obtiene del [`Context`](crate::core::component::Context), donde los componentes -/// suelen registrarse usando implementaciones de métodos como -/// [`Contextual::with_child_in()`](crate::core::component::Contextual::with_child_in). +/// Una [`RegionName`] representa un contenedor lógico identificado por un nombre de región. Su +/// contenido se obtiene del [`Context`], donde los componentes suelen registrarse usando +/// implementaciones de métodos como [`Contextual::with_child_in()`]. /// /// El contenido de una región viene determinado únicamente por su nombre, no por su tipo. Distintas /// implementaciones de [`RegionName`] que devuelvan el mismo nombre comparten el mismo conjunto de -/// componentes registrados en el [`Context`](crate::core::component::Context). Un *enum* propio que -/// implemente [`RegionName`] está pensado para **añadir** regiones que PageTop no ofrece (con un -/// nombre propio que no colisione con los de [`CoreRegion`] o -/// [`ReservedRegion`](crate::response::ReservedRegion)). +/// componentes registrados en el [`Context`]. Un *enum* propio que implemente [`RegionName`] está +/// pensado para **añadir** regiones que PageTop no ofrece (con un nombre propio que no colisione +/// con los de [`CoreRegions`] o [`ReservedRegions`]). /// /// El tema decide qué regiones mostrar en el ``, normalmente usando una plantilla -/// ([`TemplateName`]) al renderizar la página ([`Page`](crate::response::Page)). +/// ([`TemplateName`]) al renderizar la página ([`Page`]). /// /// Requiere [`AnyInfo`] para que un [`RegionRef`] pueda recuperarse mediante -/// [`AnyCast::downcast_ref()`](crate::core::AnyCast::downcast_ref) hacia su tipo concreto (por -/// ejemplo, para que un tema distinga en -/// [`Theme::handle_component()`](crate::core::theme::Theme::handle_component) qué variante -/// concreta está renderizando el componente [`Region`](crate::base::component::layout::Region)). +/// [`AnyCast::downcast_ref()`] hacia su tipo concreto (por ejemplo, para que un tema distinga en +/// [`Theme::handle_component()`] qué variante concreta está renderizando el componente [`Region`]). +/// +/// [`Context`]: crate::core::component::Context +/// [`Contextual::with_child_in()`]: crate::core::component::Contextual::with_child_in +/// [`ReservedRegions`]: crate::response::ReservedRegions +/// [`Page`]: crate::response::Page +/// [`AnyCast::downcast_ref()`]: crate::core::AnyCast::downcast_ref +/// [`Theme::handle_component()`]: crate::core::theme::Theme::handle_component +/// [`Region`]: crate::base::component::layout::Region pub trait RegionName: Send + Sync + AnyInfo { /// Devuelve el nombre de la región. /// @@ -156,24 +161,34 @@ pub trait RegionName: Send + Sync + AnyInfo { /// Referencia estática a una región. pub type RegionRef = &'static dyn RegionName; -// **< CoreRegion >********************************************************************************* +// **< CoreRegions >******************************************************************************** /// Regiones básicas que PageTop proporciona por defecto. /// -/// Comparten sus nombres (`"header"`, `"content"`, `"footer"`) con otras regiones que implementen -/// [`RegionName`], por lo que comparten también el contenido registrado bajo esos nombres. Por -/// defecto, son las regiones usadas por [`Template`](crate::base::component::layout::Template). +/// Comparten sus nombres (`"header"`, `"aside"`, `"content"`, `"footer"`) con otras regiones que +/// implementen [`RegionName`], por lo que comparten también el contenido registrado bajo esos +/// nombres. Por defecto, son las regiones usadas por [`Template`]. /// -/// A estas regiones hay que sumar también las regiones internas reservadas por -/// [`ReservedRegion`](crate::response::ReservedRegion) (`"page-top"` y `"page-bottom"`), que -/// [`Page::render()`](crate::response::Page::render) renderiza en cualquier caso. +/// A estas regiones hay que sumar también las regiones internas reservadas por [`ReservedRegions`] +/// (`"page-top"` y `"page-bottom"`), que [`Page::render()`] renderiza en cualquier caso. +/// +/// [`Template`]: crate::base::component::layout::Template +/// [`ReservedRegions`]: crate::response::ReservedRegions +/// [`Page::render()`]: crate::response::Page::render #[derive(AutoDefault)] -pub enum CoreRegion { +pub enum CoreRegions { /// Región estándar para la **cabecera** del documento, de nombre `"header"`. /// /// Suele emplearse para mostrar un logotipo, navegación principal, barras superiores, etc. Header, + /// Región de **contenido secundario**, de nombre `"aside"`. + /// + /// Se renderiza por defecto entre `Header` y `Content`. Un tema podría maquetarla, por ejemplo, + /// como columna lateral junto a `Content` y emplearla para menús secundarios o cualquier otro + /// contenido complementario al principal. + Aside, + /// Región principal de **contenido**, de nombre `"content"`. /// /// Es la región donde se renderiza el contenido principal del documento. En general será la @@ -187,11 +202,12 @@ pub enum CoreRegion { Footer, } -impl RegionName for CoreRegion { +impl RegionName for CoreRegions { #[inline] fn name(&self) -> &'static str { match self { Self::Header => "header", + Self::Aside => "aside", Self::Content => "content", Self::Footer => "footer", } @@ -200,9 +216,10 @@ impl RegionName for CoreRegion { #[inline] fn label(&self) -> L10n { match self { - Self::Header => L10n::l("region-header"), - Self::Content => L10n::l("region-content"), - Self::Footer => L10n::l("region-footer"), + Self::Header => L10n::l("region_header"), + Self::Aside => L10n::l("region_aside"), + Self::Content => L10n::l("region_content"), + Self::Footer => L10n::l("region_footer"), } } } @@ -231,11 +248,11 @@ pub trait TemplateName: Send + Sync + AnyInfo { /// Referencia estática a una plantilla. pub type TemplateRef = &'static dyn TemplateName; -// **< CoreTemplate >******************************************************************************* +// **< CoreTemplates >****************************************************************************** /// Plantillas que PageTop proporciona por defecto. #[derive(AutoDefault)] -pub enum CoreTemplate { +pub enum CoreTemplates { /// Plantilla predeterminada, de nombre `"standard"`. /// /// Se emplea cuando no se selecciona ninguna otra plantilla explícitamente. @@ -248,7 +265,7 @@ pub enum CoreTemplate { Admin, } -impl TemplateName for CoreTemplate { +impl TemplateName for CoreTemplates { #[inline] fn name(&self) -> &'static str { match self { diff --git a/src/core/theme/definition.rs b/src/core/theme/definition.rs index 3a120c4e..822cd9a9 100644 --- a/src/core/theme/definition.rs +++ b/src/core/theme/definition.rs @@ -3,7 +3,7 @@ use crate::base::component::{Html, Intro, IntroOpening, layout}; use crate::core::component::{ChildOp, Component, ComponentError, ComponentRender}; use crate::core::component::{Context, Contextual}; use crate::core::extension::Extension; -use crate::core::theme::CoreRegion; +use crate::core::theme::CoreRegions; use crate::global; use crate::html::{Markup, html}; use crate::locale::L10n; @@ -84,9 +84,10 @@ pub trait Theme: Extension + Send + Sync { /// regiones. /// /// Con la configuración por defecto, la plantilla estándar utiliza las regiones - /// [`CoreRegion::Header`](crate::core::theme::CoreRegion::Header), - /// [`CoreRegion::Content`](crate::core::theme::CoreRegion::Content) y - /// [`CoreRegion::Footer`](crate::core::theme::CoreRegion::Footer) en ese orden. + /// [`CoreRegions::Header`](crate::core::theme::CoreRegions::Header), + /// [`CoreRegions::Aside`](crate::core::theme::CoreRegions::Aside), + /// [`CoreRegions::Content`](crate::core::theme::CoreRegions::Content) y + /// [`CoreRegions::Footer`](crate::core::theme::CoreRegions::Footer) en ese orden. /// /// Los temas pueden sobrescribir este método para: /// @@ -198,7 +199,7 @@ pub trait Theme: Extension + Send + Sync { /// component: &mut dyn Component, /// cx: &mut Context, /// ) -> Option> { - /// // Solo mutación: ajusta el componente y deja que otro nivel lo renderice. + /// // Sólo mutación: ajusta el componente y deja que otro nivel lo renderice. /// setup_component!(component, { /// Button => |btn| { btn.add_class("btn-primary"); }, /// }); @@ -221,15 +222,15 @@ pub trait Theme: Extension + Send + Sync { /// Contenido predefinido para la página de error "*403 - Forbidden*" (acceso denegado). /// /// Normalmente se renderiza con la plantilla ya activa en la página (por ejemplo - /// [`CoreTemplate::Standard`](crate::core::theme::CoreTemplate::Standard)), para que el usuario - /// no pierda el contexto de navegación del sitio. Los temas pueden sobrescribir este método - /// para personalizar completamente el diseño y el contenido de la página de error. + /// [`CoreTemplates::Standard`](crate::core::theme::CoreTemplates::Standard)), para que el + /// usuario no pierda el contexto de navegación del sitio. Los temas pueden sobrescribir este + /// método para personalizar completamente el diseño y el contenido de la página de error. fn error_403(&self, page: &mut Page) { if let Some(parent) = self.parent() { return parent.error_403(page); } page.alter_title(L10n::l("error403_title")).alter_child_in( - &CoreRegion::Content, + &CoreRegions::Content, ChildOp::Prepend( Html::with(move |cx| { html! { @@ -247,7 +248,7 @@ pub trait Theme: Extension + Send + Sync { /// Contenido predefinido para la página de error "*404 - Not Found*" (recurso no encontrado). /// /// Normalmente se renderiza con la plantilla ya activa en la página (por ejemplo - /// [`CoreTemplate::Standard`](crate::core::theme::CoreTemplate::Standard)). Los temas pueden + /// [`CoreTemplates::Standard`](crate::core::theme::CoreTemplates::Standard)). Los temas pueden /// sobrescribir este método para personalizar completamente el diseño y el contenido de la /// página de error. fn error_404(&self, page: &mut Page) { @@ -255,7 +256,7 @@ pub trait Theme: Extension + Send + Sync { return parent.error_404(page); } page.alter_title(L10n::l("error404_title")).alter_child_in( - &CoreRegion::Content, + &CoreRegions::Content, ChildOp::Prepend( Html::with(move |cx| { html! { @@ -272,19 +273,15 @@ pub trait Theme: Extension + Send + Sync { /// Permite al tema preparar y componer una página de **error fatal controlado**. /// - /// Esta función decide explícitamente devolver - /// [`ErrorPage::BadRequest`](crate::response::ErrorPage::BadRequest), - /// [`ErrorPage::InternalError`](crate::response::ErrorPage::InternalError), - /// [`ErrorPage::ServiceUnavailable`](crate::response::ErrorPage::ServiceUnavailable) o - /// [`ErrorPage::GatewayTimeout`](crate::response::ErrorPage::GatewayTimeout) porque algo ha - /// fallado, pero el servidor sigue activo y el tema, el renderizado y el resto de componentes - /// funcionan con normalidad. + /// Devuelve explícitamente [`ErrorPage::BadRequest`], [`ErrorPage::InternalError`], + /// [`ErrorPage::ServiceUnavailable`] o [`ErrorPage::GatewayTimeout`], porque algo ha fallado, + /// pero el servidor sigue activo y el tema, el renderizado y el resto de componentes funcionan + /// con normalidad. /// /// Por defecto, asigna el título al documento (`title`), se renderiza con la plantilla ya - /// activa en la página (normalmente - /// [`CoreTemplate::Standard`](crate::core::theme::CoreTemplate::Standard)) y muestra un - /// componente [`Intro`] con el código HTTP del error (`code`) y los mensajes proporcionados - /// (`alert` y `help`) como descripción del error. + /// activa en la página (normalmente [`CoreTemplates::Standard`]) y muestra un componente + /// [`Intro`] con el código HTTP del error (`code`) y los mensajes proporcionados (`alert` y + /// `help`) como descripción del error. /// /// Este método no se utiliza en las implementaciones predefinidas de [`Self::error_403()`] ni /// [`Self::error_404()`], que definen su propio contenido específico. @@ -296,12 +293,18 @@ pub trait Theme: Extension + Send + Sync { /// /// Los temas pueden sobrescribir este método para personalizar el diseño y el contenido de la /// página de error. + /// + /// [`ErrorPage::BadRequest`]: crate::response::ErrorPage::BadRequest + /// [`ErrorPage::InternalError`]: crate::response::ErrorPage::InternalError + /// [`ErrorPage::ServiceUnavailable`]: crate::response::ErrorPage::ServiceUnavailable + /// [`ErrorPage::GatewayTimeout`]: crate::response::ErrorPage::GatewayTimeout + /// [`CoreTemplates::Standard`]: crate::core::theme::CoreTemplates::Standard fn error_fatal(&self, page: &mut Page, code: StatusCode, title: L10n, alert: L10n, help: L10n) { if let Some(parent) = self.parent() { return parent.error_fatal(page, code, title, alert, help); } page.alter_title(title).alter_child_in( - &CoreRegion::Content, + &CoreRegions::Content, ChildOp::Prepend( Intro::new() .with_title(L10n::l("error_code").with_arg("code", code.to_string())) diff --git a/src/core/theme/regions.rs b/src/core/theme/regions.rs index 9543b31b..4c8db31f 100644 --- a/src/core/theme/regions.rs +++ b/src/core/theme/regions.rs @@ -1,5 +1,5 @@ use crate::core::component::{Child, ChildOp, Children, Component}; -use crate::core::theme::{CoreRegion, RegionRef, ThemeRef}; +use crate::core::theme::{CoreRegions, RegionRef, ThemeRef}; use crate::{AutoDefault, UniqueId, builder_fn}; use parking_lot::RwLock; @@ -132,13 +132,13 @@ impl ChildrenInRegions { /// InRegion::Content.add(Html::with(|_| html! { "🎉 ¡Bienvenido!" })); /// /// // Texto en la cabecera, visible en todos los temas. -/// InRegion::Global(&CoreRegion::Header).add(Html::with(|_| html! { "Publicidad" })); +/// InRegion::Global(&CoreRegions::Header).add(Html::with(|_| html! { "Publicidad" })); /// ``` pub enum InRegion { /// Región principal de **contenido** por defecto. /// /// Añade el componente a la región lógica de contenido principal de la aplicación. Internamente - /// equivale a `InRegion::Global(&CoreRegion::Content)`. + /// equivale a `InRegion::Global(&CoreRegions::Content)`. Content, /// Región global compartida por todos los temas. /// @@ -173,19 +173,19 @@ impl InRegion { /// })); /// /// // Texto en la cabecera. - /// InRegion::Global(&CoreRegion::Header).add(Html::with(|_| { + /// InRegion::Global(&CoreRegions::Header).add(Html::with(|_| { /// html! { "Publicidad" } /// })); /// /// // Contenido sólo para la región del pie de página en un tema concreto. - /// InRegion::ForTheme(&theme::Basic, &CoreRegion::Footer).add(Html::with(|_| { + /// InRegion::ForTheme(&theme::Basic, &CoreRegions::Footer).add(Html::with(|_| { /// html! { "Aviso legal" } /// })); /// ``` pub fn add(&self, component: impl Component) -> &Self { let proto: Arc = Arc::new(component); match self { - InRegion::Content => Self::add_to_common(&CoreRegion::Content, proto), + InRegion::Content => Self::add_to_common(&CoreRegions::Content, proto), InRegion::Global(region) => Self::add_to_common(*region, proto), InRegion::ForTheme(theme, region) => { THEME_REGIONS diff --git a/src/response/page.rs b/src/response/page.rs index d57e8288..500e5b9d 100644 --- a/src/response/page.rs +++ b/src/response/page.rs @@ -10,7 +10,7 @@ //! composición del `` y del ``, y se ejecutan las acciones registradas por las //! extensiones antes y después de generar los contenidos. //! -//! También define las regiones internas reservadas ([`ReservedRegion`]) que actúan como puntos de +//! También define las regiones internas reservadas ([`ReservedRegions`]) que actúan como puntos de //! anclaje globales al inicio y al final del ``, fuera de las regiones que maqueta la //! plantilla activa. @@ -23,7 +23,7 @@ use crate::base::action; use crate::base::component::layout; use crate::core::component::{AssetsOp, ChildOp, ComponentRender}; use crate::core::component::{Context, ContextError, Contextual}; -use crate::core::theme::{CoreRegion, RegionName, RegionRef, TemplateRef, ThemeRef}; +use crate::core::theme::{CoreRegions, RegionName, RegionRef, TemplateRef, ThemeRef}; use crate::html::{Assets, Favicon, JavaScript, StyleSheet}; use crate::html::{Attr, Props, PropsOp}; use crate::html::{DOCTYPE, Markup, html}; @@ -31,7 +31,7 @@ use crate::locale::{CharacterDirection, L10n, LangId, LanguageIdentifier}; use crate::web::HttpRequest; use crate::{AutoDefault, builder_fn}; -// **< ReservedRegion >***************************************************************************** +// **< ReservedRegions >**************************************************************************** /// Regiones internas reservadas como puntos de anclaje globales. /// @@ -41,7 +41,7 @@ use crate::{AutoDefault, builder_fn}; /// [`Theme::render_page_body()`](crate::core::theme::Theme::render_page_body). **No suelen usarse /// como regiones "visibles" en los temas**, sino para inyectar contenido global o técnico. #[derive(AutoDefault)] -pub enum ReservedRegion { +pub enum ReservedRegions { /// Región interna situada al **inicio del ``**, de nombre `"page-top"`. /// /// Proporciona un contenedor donde las extensiones puedan inyectar elementos auxiliares antes @@ -63,7 +63,7 @@ pub enum ReservedRegion { PageBottom, } -impl RegionName for ReservedRegion { +impl RegionName for ReservedRegions { #[inline] fn name(&self) -> &'static str { match self { @@ -109,13 +109,13 @@ impl Page { } } - /// Crea una nueva instancia de página con la plantilla [`CoreTemplate::Admin`]. + /// Crea una nueva instancia de página con la plantilla [`CoreTemplates::Admin`]. /// /// Cada tema puede maquetarla de forma distinta capturando /// [`Template`](crate::base::component::layout::Template) en `handle_component()`, pero la /// plantilla en sí es la misma constante para cualquier tema. /// - /// [`CoreTemplate::Admin`]: crate::core::theme::CoreTemplate::Admin + /// [`CoreTemplates::Admin`]: crate::core::theme::CoreTemplates::Admin pub fn admin(request: HttpRequest) -> Self { Page { context: Context::admin(request), @@ -196,10 +196,10 @@ impl Page { /// 2. Despacha [`action::page::BeforeRenderBody`] para que otras extensiones puedan realizar /// ajustes previos sobre la página. /// 3. **Construye el contenido del ``**: - /// - Renderiza la región reservada superior ([`ReservedRegion::PageTop`]). + /// - Renderiza la región reservada superior ([`ReservedRegions::PageTop`]). /// - Llama a [`Theme::render_page_body()`](crate::core::theme::Theme::render_page_body) para /// renderizar las regiones del cuerpo principal de la página. - /// - Renderiza la región reservada inferior ([`ReservedRegion::PageBottom`]). + /// - Renderiza la región reservada inferior ([`ReservedRegions::PageBottom`]). /// 4. Ejecuta /// [`Theme::after_render_page_body()`](crate::core::theme::Theme::after_render_page_body) /// para que el tema pueda aplicar ajustes finales. @@ -221,9 +221,9 @@ impl Page { // Renderiza el . let body = html! { - (layout::Region::of(&ReservedRegion::PageTop).render(&mut self.context).await) + (layout::Region::of(&ReservedRegions::PageTop).render(&mut self.context).await) (self.context.theme().render_page_body(self).await) - (layout::Region::of(&ReservedRegion::PageBottom).render(&mut self.context).await) + (layout::Region::of(&ReservedRegions::PageBottom).render(&mut self.context).await) }; // Acciones específicas del tema después de renderizar el . @@ -314,7 +314,8 @@ impl Contextual for Page { #[builder_fn] fn with_child(mut self, op: impl Into) -> Self { - self.context.alter_child_in(&CoreRegion::Content, op.into()); + self.context + .alter_child_in(&CoreRegions::Content, op.into()); self } diff --git a/tests/component_template.rs b/tests/component_template.rs index 725cd8a4..fbf0bc8b 100644 --- a/tests/component_template.rs +++ b/tests/component_template.rs @@ -11,7 +11,7 @@ async fn setup() { // **< A theme that intercepts the `Template` component >******************************************* /// Replaces the default `Template` composition (`Header` + `Content` + `Footer`) with a fixed -/// marker string, for both `CoreTemplate::Standard` and `CoreTemplate::Admin`. Mirrors how +/// marker string, for both `CoreTemplates::Standard` and `CoreTemplates::Admin`. Mirrors how /// a real theme (e.g. `pagetop-bootsier`) tells its own layout apart from PageTop's default: by /// intercepting the `Template` component in `handle_component()`, not by swapping which /// `TemplateRef` gets resolved. @@ -32,7 +32,7 @@ impl Theme for MarkerTheme { _cx: &mut Context, ) -> Option> { let template = (&*component).downcast_ref::()?; - template.template().downcast_ref::()?; + template.template().downcast_ref::()?; Some(Ok(html! { "marker-template-output" })) } } @@ -40,7 +40,7 @@ impl Theme for MarkerTheme { // **< Default/Admin template identity is independent of the active theme >************************* // // `Theme::default_template()`/`admin_template()` were removed: `Context::template()` always -// resolves `Default`/`Admin` to the core `CoreTemplate::Standard`/`Admin` identity, regardless +// resolves `Default`/`Admin` to the core `CoreTemplates::Standard`/`Admin` identity, regardless // of which theme is active. Themes customize the actual rendering by intercepting the `Template` // component in `handle_component()` instead (see the tests further below). @@ -68,7 +68,7 @@ async fn explicit_template_is_not_overridden_by_a_later_with_theme() { // A template explicitly set with `with_template()` prevails even if `with_theme()` is called // afterwards. let cx = Context::default() - .with_template(&CoreTemplate::Admin) + .with_template(&CoreTemplates::Admin) .with_theme(&pagetop::base::theme::Basic); assert_eq!(cx.template().name(), "admin");