♻️ 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,18 +1,206 @@
|
|||
//! API para añadir y gestionar nuevos temas.
|
||||
//!
|
||||
//! En PageTop un tema es la *piel* de la aplicación. Es responsable último de los estilos,
|
||||
//! tipografías, espaciados y cualquier otro detalle visual o interactivo (animaciones, scripts de
|
||||
//! interfaz, etc.).
|
||||
//! Los temas son extensiones que implementan [`Extension`](crate::core::extension::Extension) y
|
||||
//! también [`Theme`], de modo que [`Extension::theme()`](crate::core::extension::Extension::theme)
|
||||
//! permita identificar y registrar los temas disponibles.
|
||||
//!
|
||||
//! Un tema determina el aspecto final de un documento HTML sin alterar la lógica interna de los
|
||||
//! componentes ni la estructura del documento, que queda definida por la plantilla
|
||||
//! ([`Template`](crate::base::component::Template)) utilizada por cada página.
|
||||
//! Un tema es la *piel* de la aplicación: define estilos, tipografías, espaciados o comportamientos
|
||||
//! interactivos. Para ello utiliza plantillas ([`Template`]) que describen cómo maquetar el cuerpo
|
||||
//! del documento a partir de varias regiones ([`Region`]). Cada región es un contenedor lógico
|
||||
//! identificado por un nombre, cuyo contenido se obtiene del [`Context`] de la página.
|
||||
//!
|
||||
//! Los temas son extensiones que implementan [`Extension`](crate::core::extension::Extension), por
|
||||
//! lo que se instancian, declaran dependencias y se inician igual que cualquier otra extensión.
|
||||
//! También deben implementar [`Theme`] y sobrescribir el método
|
||||
//! [`Extension::theme()`](crate::core::extension::Extension::theme) para que PageTop pueda
|
||||
//! registrarlos como temas.
|
||||
//! Una página ([`Page`](crate::response::page::Page)) representa un documento HTML completo.
|
||||
//! Implementa [`Contextual`](crate::core::component::Contextual) para gestionar su propio
|
||||
//! [`Context`], donde mantiene el tema activo, la plantilla seleccionada y los componentes
|
||||
//! asociados a cada región.
|
||||
//!
|
||||
//! De este modo, temas y extensiones colaboran sobre una estructura común: las aplicaciones
|
||||
//! registran componentes en el [`Context`], las plantillas organizan las regiones y las páginas
|
||||
//! generan el documento HTML resultante.
|
||||
//!
|
||||
//! Los temas pueden definir sus propias implementaciones de [`Template`] y [`Region`] (por ejemplo,
|
||||
//! mediante *enums* adicionales) para añadir nuevas plantillas o exponer regiones específicas.
|
||||
|
||||
use crate::core::component::Context;
|
||||
use crate::html::{html, Markup};
|
||||
use crate::locale::L10n;
|
||||
use crate::{join, AutoDefault};
|
||||
|
||||
// **< Region >*************************************************************************************
|
||||
|
||||
/// Interfaz común para las regiones lógicas de un documento.
|
||||
///
|
||||
/// Una `Region` 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()`](crate::core::component::Contextual::with_child_in).
|
||||
///
|
||||
/// El contenido de una región viene determinado únicamente por su nombre, no por su tipo. Distintas
|
||||
/// implementaciones de [`Region`] que devuelvan el mismo nombre compartirán el mismo conjunto de
|
||||
/// componentes registrados en el [`Context`], aunque cada región puede renderizar ese contenido de
|
||||
/// forma diferente. Por ejemplo, [`DefaultRegion::Header`] y `BootsierRegion::Header` mostrarían
|
||||
/// los mismos componentes si ambas devuelven el nombre `"header"`, pero podrían maquetarse de
|
||||
/// manera distinta.
|
||||
///
|
||||
/// El tema decide qué regiones mostrar en el cuerpo del documento, normalmente usando una plantilla
|
||||
/// ([`Template`]) al renderizar la página ([`Page`](crate::response::page::Page)).
|
||||
pub trait Region {
|
||||
/// Devuelve el nombre de la región.
|
||||
///
|
||||
/// Este nombre es el identificador lógico de la región y se usa como clave en el [`Context`]
|
||||
/// para recuperar y renderizar el contenido registrado bajo ese nombre. Cualquier
|
||||
/// implementación de [`Region`] que devuelva el mismo nombre compartirá el mismo conjunto de
|
||||
/// componentes.
|
||||
///
|
||||
/// En la implementación predeterminada de [`Self::render()`] también se utiliza para construir
|
||||
/// las clases del contenedor de la región (`"region region-<name>"`).
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Devuelve la etiqueta de accesibilidad localizada asociada a la región.
|
||||
///
|
||||
/// En la implementación predeterminada de [`Self::render()`], este valor se usa como
|
||||
/// `aria-label` del contenedor de la región.
|
||||
fn label(&self) -> L10n;
|
||||
|
||||
/// Renderiza el contenedor de la región.
|
||||
///
|
||||
/// Por defecto, recupera del [`Context`] el contenido de la región y, si no está vacío, lo
|
||||
/// envuelve en un `<div>` con clases `"region region-<name>"` y un `aria-label` basado en la
|
||||
/// etiqueta localizada de la región:
|
||||
///
|
||||
/// ```html
|
||||
/// <div class="region region-<name>" role="region" aria-label="<label>">
|
||||
/// <!-- Componentes de la región "name" -->
|
||||
/// </div>
|
||||
/// ```
|
||||
///
|
||||
/// Se puede sobrescribir este método para modificar la estructura del contenedor, las clases
|
||||
/// utilizadas o la semántica del marcado generado para cada región.
|
||||
fn render(&'static self, cx: &mut Context) -> Markup
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
html! {
|
||||
@let region = cx.render_region(self);
|
||||
@if !region.is_empty() {
|
||||
div
|
||||
class=(join!("region region-", self.name()))
|
||||
role="region"
|
||||
aria-label=[self.label().lookup(cx)]
|
||||
{
|
||||
(region)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Referencia estática a una región.
|
||||
pub type RegionRef = &'static dyn Region;
|
||||
|
||||
// **< DefaultRegion >******************************************************************************
|
||||
|
||||
/// Regiones básicas que PageTop proporciona por defecto.
|
||||
///
|
||||
/// Estas regiones comparten sus nombres (`"header"`, `"content"`, `"footer"`) con cualquier región
|
||||
/// equivalente definida por otros temas, por lo que comparten también el contenido registrado bajo
|
||||
/// esos nombres.
|
||||
#[derive(AutoDefault)]
|
||||
pub enum DefaultRegion {
|
||||
/// 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 principal de **contenido**, de nombre `"content"`.
|
||||
///
|
||||
/// Es la región donde se renderiza el contenido principal del documento. En general será la
|
||||
/// región mínima imprescindible para que una página tenga sentido.
|
||||
#[default]
|
||||
Content,
|
||||
|
||||
/// Región estándar para el **pie de página**, de nombre `"footer"`.
|
||||
///
|
||||
/// Suele contener información legal, enlaces secundarios, créditos, etc.
|
||||
Footer,
|
||||
}
|
||||
|
||||
impl Region for DefaultRegion {
|
||||
#[inline]
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Header => "header",
|
||||
Self::Content => "content",
|
||||
Self::Footer => "footer",
|
||||
}
|
||||
}
|
||||
|
||||
#[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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< Template >***********************************************************************************
|
||||
|
||||
/// Interfaz común para definir plantillas de contenido.
|
||||
///
|
||||
/// Una `Template` puede proporcionar una o más variantes para decidir la composición del `<body>`
|
||||
/// de una página ([`Page`](crate::response::page::Page)). El tema utiliza esta información para
|
||||
/// determinar qué regiones ([`Region`]) deben renderizarse y en qué orden.
|
||||
pub trait Template {
|
||||
/// Renderiza el contenido de la plantilla.
|
||||
///
|
||||
/// Por defecto, renderiza las regiones básicas de [`DefaultRegion`] en este orden:
|
||||
/// [`DefaultRegion::Header`], [`DefaultRegion::Content`] y [`DefaultRegion::Footer`].
|
||||
///
|
||||
/// Se puede sobrescribir este método para:
|
||||
///
|
||||
/// - Cambiar el conjunto de regiones que se renderizan según variantes de la plantilla.
|
||||
/// - Alterar el orden de dichas regiones.
|
||||
/// - Envolver las regiones en contenedores adicionales.
|
||||
/// - Implementar distribuciones específicas (por ejemplo, con barras laterales).
|
||||
///
|
||||
/// Este método se invoca normalmente desde [`Theme::render_page_body()`] para generar el
|
||||
/// contenido del `<body>` de una página según la plantilla devuelta por el contexto de la
|
||||
/// propia página ([`Contextual::template()`](crate::core::component::Contextual::template())).
|
||||
fn render(&'static self, cx: &mut Context) -> Markup {
|
||||
html! {
|
||||
(DefaultRegion::Header.render(cx))
|
||||
(DefaultRegion::Content.render(cx))
|
||||
(DefaultRegion::Footer.render(cx))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Referencia estática a una plantilla.
|
||||
pub type TemplateRef = &'static dyn Template;
|
||||
|
||||
// **< DefaultTemplate >****************************************************************************
|
||||
|
||||
/// Plantillas que PageTop proporciona por defecto.
|
||||
#[derive(AutoDefault)]
|
||||
pub enum DefaultTemplate {
|
||||
/// Plantilla predeterminada.
|
||||
///
|
||||
/// Utiliza la implementación por defecto de [`Template::render()`] y se emplea cuando no se
|
||||
/// selecciona ninguna otra plantilla explícitamente.
|
||||
#[default]
|
||||
Standard,
|
||||
|
||||
/// Plantilla de error.
|
||||
///
|
||||
/// Se utiliza para páginas de error u otros estados excepcionales. Por defecto utiliza la misma
|
||||
/// implementación de [`Template::render()`] que [`Self::Standard`].
|
||||
Error,
|
||||
}
|
||||
|
||||
impl Template for DefaultTemplate {}
|
||||
|
||||
// **< Definitions >********************************************************************************
|
||||
|
||||
mod definition;
|
||||
pub use definition::{Theme, ThemeRef};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue