♻️ Refactoriza la gestión de regiones y plantillas
This commit is contained in:
parent
bfdc0da407
commit
f2733bb250
15 changed files with 494 additions and 655 deletions
|
|
@ -1,46 +1,8 @@
|
|||
//! Componentes nativos proporcionados por PageTop.
|
||||
//!
|
||||
//! Conviene destacar que PageTop distingue entre:
|
||||
//!
|
||||
//! - **Componentes estructurales** que definen el esqueleto de un documento HTML, como [`Template`]
|
||||
//! y [`Region`], utilizados por [`Page`](crate::response::page::Page) para generar la estructura
|
||||
//! final.
|
||||
//! - **Componentes de contenido** (menús, barras, tarjetas, etc.), que se incluyen en las regiones
|
||||
//! gestionadas por los componentes estructurales.
|
||||
//!
|
||||
//! El componente [`Template`] describe cómo maquetar el cuerpo del documento a partir de varias
|
||||
//! regiones lógicas ([`Region`]). En función de la plantilla seleccionada, determina qué regiones
|
||||
//! se renderizan y en qué orden. Por ejemplo, la plantilla predeterminada [`Template::DEFAULT`]
|
||||
//! utiliza las regiones [`Region::HEADER`], [`Region::CONTENT`] y [`Region::FOOTER`].
|
||||
//!
|
||||
//! Un componente [`Region`] es un contenedor lógico asociado a un nombre de región. Su contenido se
|
||||
//! obtiene del [`Context`](crate::core::component::Context), donde los componentes se registran
|
||||
//! mediante [`Contextual::with_child_in()`](crate::core::component::Contextual::with_child_in) y
|
||||
//! otros mecanismos similares, y se integra en el documento a través de [`Template`].
|
||||
//!
|
||||
//! Por su parte, una página ([`Page`](crate::response::page::Page)) representa un documento HTML
|
||||
//! completo. Implementa [`Contextual`](crate::core::component::Contextual) para mantener su propio
|
||||
//! [`Context`](crate::core::component::Context), donde gestiona el tema activo, la plantilla
|
||||
//! seleccionada y los componentes asociados a cada región, y se encarga de generar la estructura
|
||||
//! final de la página.
|
||||
//!
|
||||
//! De este modo, temas y extensiones colaboran sobre una estructura común: las aplicaciones
|
||||
//! registran componentes en el [`Context`](crate::core::component::Context), las plantillas
|
||||
//! organizan las regiones y las páginas generan el documento HTML resultante.
|
||||
//!
|
||||
//! Los temas pueden sobrescribir [`Template`] para exponer nuevas plantillas o adaptar las
|
||||
//! predeterminadas, y lo mismo con [`Region`] para añadir regiones adicionales o personalizar su
|
||||
//! representación.
|
||||
|
||||
mod html;
|
||||
pub use html::Html;
|
||||
|
||||
mod region;
|
||||
pub use region::Region;
|
||||
|
||||
mod template;
|
||||
pub use template::Template;
|
||||
|
||||
mod block;
|
||||
pub use block::Block;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
/// Componente estructural que renderiza el contenido de una región del documento.
|
||||
///
|
||||
/// `Region` actúa como un contenedor lógico asociado a un nombre de región. Su contenido se obtiene
|
||||
/// del contexto de renderizado ([`Context`]), donde los componentes suelen registrarse con métodos
|
||||
/// como [`Contextual::with_child_in()`]. Cada región puede integrarse posteriormente en el cuerpo
|
||||
/// del documento mediante [`Template`], normalmente desde una página ([`Page`]).
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Region {
|
||||
#[default(AttrName::new(Self::DEFAULT))]
|
||||
name: AttrName,
|
||||
#[default(L10n::l("region-content"))]
|
||||
label: L10n,
|
||||
}
|
||||
|
||||
impl Component for Region {
|
||||
fn new() -> Self {
|
||||
Region::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.name.get()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
let Some(name) = self.name().get() else {
|
||||
return PrepareMarkup::None;
|
||||
};
|
||||
let output = cx.render_region(&name);
|
||||
if output.is_empty() {
|
||||
return PrepareMarkup::None;
|
||||
}
|
||||
PrepareMarkup::With(html! {
|
||||
div
|
||||
id=[self.id()]
|
||||
class=(join!("region region-", &name))
|
||||
role="region"
|
||||
aria-label=[self.label().lookup(cx)]
|
||||
{
|
||||
(output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Region {
|
||||
/// Región especial situada al **inicio del documento**.
|
||||
///
|
||||
/// Su función es proporcionar un punto estable donde las extensiones puedan inyectar contenido
|
||||
/// global antes de renderizar el resto de regiones principales (cabecera, contenido, etc.).
|
||||
///
|
||||
/// No suele utilizarse en los temas como una región “visible” dentro del maquetado habitual,
|
||||
/// sino como punto de anclaje para elementos auxiliares, marcadores técnicos, inicializadores o
|
||||
/// contenido de depuración que deban situarse en la parte superior del documento.
|
||||
///
|
||||
/// Se considera una región **reservada** para este tipo de usos globales.
|
||||
pub const PAGETOP: &str = "page-top";
|
||||
|
||||
/// Región estándar para la **cabecera** del documento.
|
||||
///
|
||||
/// Suele emplearse para mostrar un logotipo, navegación principal, barras superiores, etc.
|
||||
pub const HEADER: &str = "header";
|
||||
|
||||
/// Región principal de **contenido**.
|
||||
///
|
||||
/// Es la región donde se espera que se renderice el contenido principal de la página (p. ej.
|
||||
/// cuerpo de la ruta actual, bloques centrales, vistas principales, etc.). En muchos temas será
|
||||
/// la región mínima imprescindible para que la página tenga sentido.
|
||||
pub const CONTENT: &str = "content";
|
||||
|
||||
/// Región estándar para el **pie de página**.
|
||||
///
|
||||
/// Suele contener información legal, enlaces secundarios, créditos, etc.
|
||||
pub const FOOTER: &str = "footer";
|
||||
|
||||
/// Región especial situada al **final del documento**.
|
||||
///
|
||||
/// Pensada para proporcionar un punto estable donde las extensiones puedan inyectar contenido
|
||||
/// global después de renderizar el resto de regiones principales (cabecera, contenido, etc.).
|
||||
///
|
||||
/// No suele utilizarse en los temas como una región “visible” dentro del maquetado habitual,
|
||||
/// sino como punto de anclaje para elementos auxiliares asociados a comportamientos dinámicos
|
||||
/// que deban situarse en la parte inferior del documento.
|
||||
///
|
||||
/// Igual que [`Self::PAGETOP`], se considera una región **reservada** para este tipo de usos
|
||||
/// globales.
|
||||
pub const PAGEBOTTOM: &str = "page-bottom";
|
||||
|
||||
/// Región por defecto que se asigna cuando no se especifica ningún nombre.
|
||||
///
|
||||
/// Por diseño, la región por defecto es la de contenido principal ([`Self::CONTENT`]), de
|
||||
/// manera que un tema sencillo pueda limitarse a definir una sola región funcional.
|
||||
pub const DEFAULT: &str = Self::CONTENT;
|
||||
|
||||
/// Prepara una región para el nombre indicado.
|
||||
///
|
||||
/// El valor de `name` se utiliza como nombre de la región y como identificador (`id`) del
|
||||
/// contenedor. Al renderizarse, este componente mostrará el contenido registrado en el contexto
|
||||
/// bajo ese nombre.
|
||||
pub fn named(name: impl AsRef<str>) -> Self {
|
||||
Region {
|
||||
name: AttrName::new(name),
|
||||
label: L10n::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepara una región para el nombre indicado con una etiqueta de accesibilidad.
|
||||
///
|
||||
/// El valor de `name` se utiliza como nombre de la región y como identificador (`id`) del
|
||||
/// contenedor, mientras que `label` será el texto localizado que se usará como `aria-label` del
|
||||
/// contenedor.
|
||||
pub fn labeled(name: impl AsRef<str>, label: L10n) -> Self {
|
||||
Region {
|
||||
name: AttrName::new(name),
|
||||
label,
|
||||
}
|
||||
}
|
||||
|
||||
// **< Region BUILDER >*************************************************************************
|
||||
|
||||
/// Establece o modifica el nombre de la región.
|
||||
#[builder_fn]
|
||||
pub fn with_name(mut self, name: impl AsRef<str>) -> Self {
|
||||
self.name.alter_value(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Establece la etiqueta localizada de la región.
|
||||
///
|
||||
/// Esta etiqueta se utiliza como `aria-label` del contenedor predefinido `<div role="region">`,
|
||||
/// lo que mejora la accesibilidad para lectores de pantalla y otras tecnologías de apoyo.
|
||||
#[builder_fn]
|
||||
pub fn with_label(mut self, label: L10n) -> Self {
|
||||
self.label = label;
|
||||
self
|
||||
}
|
||||
|
||||
// **< Region GETTERS >*************************************************************************
|
||||
|
||||
/// Devuelve el nombre de la región.
|
||||
pub fn name(&self) -> &AttrName {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Devuelve la etiqueta localizada asociada a la región.
|
||||
pub fn label(&self) -> &L10n {
|
||||
&self.label
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
/// Componente estructural para renderizar plantillas de contenido.
|
||||
///
|
||||
/// `Template` describe cómo se compone el cuerpo del documento a partir de varias regiones lógicas
|
||||
/// ([`Region`]). En función de su nombre, decide qué regiones se renderizan y en qué orden.
|
||||
///
|
||||
/// Normalmente se invoca desde una página ([`Page`]), que consulta el nombre de plantilla guardado
|
||||
/// en el [`Context`] y delega en `Template` la composición de las regiones que forman el cuerpo del
|
||||
/// documento.
|
||||
///
|
||||
/// Los temas pueden sobrescribir este componente para exponer sus propias plantillas o adaptar las
|
||||
/// plantillas predeterminadas.
|
||||
#[derive(AutoDefault)]
|
||||
pub struct Template {
|
||||
#[default(AttrName::new(Self::DEFAULT))]
|
||||
name: AttrName,
|
||||
}
|
||||
|
||||
impl Component for Template {
|
||||
fn new() -> Self {
|
||||
Template::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.name.get()
|
||||
}
|
||||
|
||||
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
|
||||
let Some(name) = self.name().get() else {
|
||||
return PrepareMarkup::None;
|
||||
};
|
||||
match name.as_str() {
|
||||
Self::DEFAULT | Self::ERROR => PrepareMarkup::With(html! {
|
||||
(Region::labeled(Region::HEADER, L10n::l("region-header")).render(cx))
|
||||
(Region::default().render(cx))
|
||||
(Region::labeled(Region::FOOTER, L10n::l("region-footer")).render(cx))
|
||||
}),
|
||||
_ => PrepareMarkup::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Template {
|
||||
/// Nombre de la plantilla predeterminada.
|
||||
///
|
||||
/// Por defecto define una estructura básica con las regiones [`Region::HEADER`],
|
||||
/// [`Region::CONTENT`] y [`Region::FOOTER`], en ese orden. Esta plantilla se usa cuando no se
|
||||
/// selecciona ninguna otra de forma explícita (ver [`Contextual::with_template()`]).
|
||||
pub const DEFAULT: &str = "default";
|
||||
|
||||
/// Nombre de la plantilla de error.
|
||||
///
|
||||
/// Se utiliza para páginas de error u otros estados excepcionales. Por defecto reutiliza
|
||||
/// la misma estructura que [`Self::DEFAULT`], pero permite a temas y extensiones distinguir
|
||||
/// el contexto de error para aplicar estilos o contenidos específicos.
|
||||
pub const ERROR: &str = "error";
|
||||
|
||||
/// Selecciona la plantilla asociada al nombre indicado.
|
||||
///
|
||||
/// El valor de `name` se utiliza como nombre de la plantilla y como identificador (`id`) del
|
||||
/// componente.
|
||||
pub fn named(name: impl AsRef<str>) -> Self {
|
||||
Template {
|
||||
name: AttrName::new(name),
|
||||
}
|
||||
}
|
||||
|
||||
// **< Template BUILDER >***********************************************************************
|
||||
|
||||
/// Establece o modifica el nombre de la plantilla seleccionada.
|
||||
#[builder_fn]
|
||||
pub fn with_name(mut self, name: impl AsRef<str>) -> Self {
|
||||
self.name.alter_value(name);
|
||||
self
|
||||
}
|
||||
|
||||
// **< Template GETTERS >***********************************************************************
|
||||
|
||||
/// Devuelve el nombre de la plantilla seleccionada.
|
||||
pub fn name(&self) -> &AttrName {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ impl Theme for Basic {
|
|||
.with_weight(-99),
|
||||
))
|
||||
.alter_child_in(
|
||||
Region::FOOTER,
|
||||
&DefaultRegion::Footer,
|
||||
ChildOp::AddIfEmpty(Child::with(PoweredBy::new())),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue