✨ (theme): Añade ColorName y CoreColors
Nuevo trait `ColorName` para que cada tema aporte su propia paleta de colores; `CoreColors` es la paleta por defecto. Aprovecha para refactorizar `core/theme.rs` en submódulos por concepto.
This commit is contained in:
parent
1453764462
commit
bb6855e41f
5 changed files with 331 additions and 286 deletions
|
|
@ -10,18 +10,20 @@
|
|||
--val-fw--base: 400;
|
||||
/* Line height */
|
||||
--val-lh--base: 1.5;
|
||||
/* Colors */
|
||||
--val-color--bg: #fafafa;
|
||||
--val-color--text: #212529;
|
||||
--val-color--text--muted: color-mix(in srgb, var(--val-color--text) 50%, var(--val-color--bg));
|
||||
--val-color--border: #212529;
|
||||
/* Core Colors */
|
||||
--val-color--primary: #0d6efd;
|
||||
--val-color--secondary: #6c757d;
|
||||
--val-color--light: #dee2e6;
|
||||
--val-color--success: #198754;
|
||||
--val-color--info: #0dcaf0;
|
||||
--val-color--warning: #ffc107;
|
||||
--val-color--danger: #dc3545;
|
||||
/* Other Colors */
|
||||
--val-color--bg: #fafafa;
|
||||
--val-color--bg--muted: #dee2e6;
|
||||
--val-color--text: #212529;
|
||||
--val-color--text--muted: color-mix(in srgb, var(--val-color--text) 50%, var(--val-color--bg));
|
||||
--val-color--switch-off: #adb5bd;
|
||||
--val-color--border: #212529;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
|
|
|
|||
|
|
@ -117,285 +117,12 @@
|
|||
//!
|
||||
//! [`ReservedRegions`]: crate::response::ReservedRegions
|
||||
|
||||
use crate::AutoDefault;
|
||||
use crate::core::AnyInfo;
|
||||
use crate::locale::Lc;
|
||||
mod color;
|
||||
pub use color::{ColorName, CoreColors};
|
||||
|
||||
// **< RegionName >*********************************************************************************
|
||||
|
||||
/// Interfaz común para las regiones lógicas del `<body>`.
|
||||
///
|
||||
/// 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`]. 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 `<body>`, normalmente usando una plantilla
|
||||
/// ([`TemplateName`]) al renderizar la página ([`Page`]).
|
||||
///
|
||||
/// Requiere [`AnyInfo`] para que un [`RegionRef`] pueda recuperarse mediante
|
||||
/// [`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.
|
||||
///
|
||||
/// Este nombre es el identificador lógico de la región y se usa como clave en el
|
||||
/// [`Context`](crate::core::component::Context) para recuperar y renderizar el contenido
|
||||
/// registrado bajo ese nombre. Cualquier implementación de [`RegionName`] que devuelva el mismo
|
||||
/// nombre compartirá el mismo conjunto de componentes.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Devuelve un *texto localizado* como etiqueta de accesibilidad asociada a la región.
|
||||
///
|
||||
/// En la implementación predeterminada de [`Region`](crate::base::component::layout::Region),
|
||||
/// este valor se usa como `aria-label` del contenedor de la región.
|
||||
fn label(&self) -> Lc;
|
||||
}
|
||||
|
||||
/// Referencia estática a una región.
|
||||
pub type RegionRef = &'static dyn RegionName;
|
||||
|
||||
// **< CoreRegions >********************************************************************************
|
||||
|
||||
/// Regiones básicas que PageTop proporciona por defecto.
|
||||
///
|
||||
/// 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 [`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 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
|
||||
/// 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 RegionName for CoreRegions {
|
||||
#[inline]
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Header => "header",
|
||||
Self::Aside => "aside",
|
||||
Self::Content => "content",
|
||||
Self::Footer => "footer",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn label(&self) -> Lc {
|
||||
match self {
|
||||
Self::Header => Lc::l("region_header"),
|
||||
Self::Aside => Lc::l("region_aside"),
|
||||
Self::Content => Lc::l("region_content"),
|
||||
Self::Footer => Lc::l("region_footer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< TemplateName >*******************************************************************************
|
||||
|
||||
/// Interfaz común para las plantillas lógicas de una página.
|
||||
///
|
||||
/// Representa una variante identificada por un nombre. Un tema puede usar este nombre para decidir
|
||||
/// la composición del cuerpo de una página ([`Page`](crate::response::Page)), es decir, qué
|
||||
/// regiones ([`RegionName`]) renderizar y en qué orden.
|
||||
///
|
||||
/// Requiere [`AnyInfo`] por el mismo motivo que [`RegionName`], para que un [`TemplateRef`] 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 [`Template`](crate::base::component::layout::Template)).
|
||||
pub trait TemplateName: Send + Sync + AnyInfo {
|
||||
/// Devuelve el nombre de la plantilla.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Devuelve un *texto localizado* como etiqueta descriptiva de la plantilla.
|
||||
fn label(&self) -> Lc;
|
||||
}
|
||||
|
||||
/// Referencia estática a una plantilla.
|
||||
pub type TemplateRef = &'static dyn TemplateName;
|
||||
|
||||
// **< CoreTemplates >******************************************************************************
|
||||
|
||||
/// Plantillas que PageTop proporciona por defecto.
|
||||
#[derive(AutoDefault)]
|
||||
pub enum CoreTemplates {
|
||||
/// Plantilla predeterminada, de nombre `"standard"`.
|
||||
///
|
||||
/// Se emplea cuando no se selecciona ninguna otra plantilla explícitamente.
|
||||
#[default]
|
||||
Standard,
|
||||
|
||||
/// Plantilla para la **interfaz de administración**, de nombre `"admin"`.
|
||||
///
|
||||
/// Se utiliza para páginas de administración o paneles de control.
|
||||
Admin,
|
||||
}
|
||||
|
||||
impl TemplateName for CoreTemplates {
|
||||
#[inline]
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Standard => "standard",
|
||||
Self::Admin => "admin",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn label(&self) -> Lc {
|
||||
match self {
|
||||
Self::Standard => Lc::l("template-standard"),
|
||||
Self::Admin => Lc::l("template-admin"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< render_component! >**************************************************************************
|
||||
|
||||
/// Sobrescribe el renderizado de componentes en
|
||||
/// [`Theme::handle_component()`](crate::core::theme::Theme::handle_component).
|
||||
///
|
||||
/// Evalúa `$component` contra cada tipo de componente listado en orden. En cuanto encuentra
|
||||
/// coincidencia, devuelve `Some(Ok(markup))` o `Some(Err(e))` según el resultado de la expresión
|
||||
/// asociada. Si ningún tipo coincide, devuelve `None` para que el sistema continúe con la cadena de
|
||||
/// herencia o con el renderizado por defecto del propio componente.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
/// &self,
|
||||
/// component: &dyn Component,
|
||||
/// cx: &mut Context,
|
||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||
/// render_component!(component, {
|
||||
/// Button => |btn| { Ok(html! { button.btn.btn-primary { (btn.label()) } }) },
|
||||
/// Heading => |h| self.render_heading(h, cx),
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// fn render_heading(&self, h: &Heading, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
/// Ok(html! { h2.display-4 { (h.text()) } })
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! render_component {
|
||||
($component:expr, { $($type:ty => |$var:ident| $body:expr),* $(,)? }) => {
|
||||
'render_component: {
|
||||
// Reborrow explícito como referencia compartida para que `downcast_ref` funcione
|
||||
// correctamente con `&mut dyn Component` (limitación del compilador con trait objects).
|
||||
let __c = &*($component);
|
||||
$(
|
||||
if let Some($var) = __c.downcast_ref::<$type>() {
|
||||
break 'render_component Some($body);
|
||||
}
|
||||
)*
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// **< setup_component! >***************************************************************************
|
||||
|
||||
/// Muta un componente dentro de
|
||||
/// [`Theme::handle_component()`](crate::core::theme::Theme::handle_component).
|
||||
///
|
||||
/// Evalúa `$component` contra cada tipo de componente listado en orden. En cuanto encuentra
|
||||
/// coincidencia, ejecuta el bloque asociado y detiene la evaluación. Si ningún tipo coincide, no
|
||||
/// hace nada.
|
||||
///
|
||||
/// Usa acceso mutable al componente mediante [`downcast_mut`](crate::core::AnyCast::downcast_mut),
|
||||
/// lo que permite modificar su estado. El tema puede devolver `None` tras la mutación para que otro
|
||||
/// nivel de la cadena se encargue del renderizado.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// Solo mutación: el tema ajusta el componente y delega el renderizado al siguiente nivel:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
/// &self,
|
||||
/// component: &mut dyn Component,
|
||||
/// cx: &mut Context,
|
||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||
/// setup_component!(component, { Button => |btn| { btn.add_class("btn-primary"); } });
|
||||
/// None
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Mutación y renderizado combinados: el `Button` se muta y se renderiza aquí; el `Heading` se
|
||||
/// muta pero continúa la cadena para que otro nivel lo renderice:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
/// &self,
|
||||
/// component: &mut dyn Component,
|
||||
/// cx: &mut Context,
|
||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||
/// setup_component!(component, {
|
||||
/// Button => |btn| { btn.add_class("btn-primary"); },
|
||||
/// Heading => |h| { h.add_class("display-4"); },
|
||||
/// });
|
||||
/// render_component!(component, {
|
||||
/// Button => |btn| Ok(html! { button.btn { (btn.label()) } }),
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! setup_component {
|
||||
($component:expr, { $($type:ty => |$var:ident| $body:expr),* $(,)? }) => {
|
||||
'setup_component: {
|
||||
$(
|
||||
if let Some($var) = ($component).downcast_mut::<$type>() {
|
||||
$body;
|
||||
break 'setup_component;
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// **< Definitions >********************************************************************************
|
||||
mod layout;
|
||||
pub use layout::{CoreRegions, RegionName, RegionRef};
|
||||
pub use layout::{CoreTemplates, TemplateName, TemplateRef};
|
||||
|
||||
mod definition;
|
||||
pub use definition::{Theme, ThemeRef};
|
||||
|
|
|
|||
40
src/core/theme/color.rs
Normal file
40
src/core/theme/color.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use crate::AutoDefault;
|
||||
|
||||
// **< ColorName >**********************************************************************************
|
||||
|
||||
/// Interfaz común para los colores de la paleta de un tema.
|
||||
///
|
||||
/// PageTop ofrece una implementación predeterminada en [`CoreColors`], aunque probablemente cada
|
||||
/// tema proporcionará su propia lista de colores implementando este *trait*.
|
||||
pub trait ColorName {
|
||||
/// Devuelve el nombre asociado al color (p. ej. `"primary"`, `"danger"`, etc.). Normalmente se
|
||||
/// usará para generar la clase CSS del componente.
|
||||
fn name(self) -> &'static str;
|
||||
}
|
||||
|
||||
// **< CoreColors >*********************************************************************************
|
||||
|
||||
/// Paleta de colores predeterminada de PageTop.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
pub enum CoreColors {
|
||||
#[default]
|
||||
Primary,
|
||||
Secondary,
|
||||
Success,
|
||||
Info,
|
||||
Warning,
|
||||
Danger,
|
||||
}
|
||||
|
||||
impl ColorName for CoreColors {
|
||||
fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Primary => "primary",
|
||||
Self::Secondary => "secondary",
|
||||
Self::Success => "success",
|
||||
Self::Info => "info",
|
||||
Self::Warning => "warning",
|
||||
Self::Danger => "danger",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,8 +195,9 @@ pub trait Theme: Extension + Send + Sync {
|
|||
/// - `Some(Ok(markup))` con el HTML generado por el tema para el componente.
|
||||
/// - `Some(Err(e))` si el tema intentó renderizarlo pero falló.
|
||||
///
|
||||
/// Para renderizar usa [`render_component!`], que devuelve `None` si ningún tipo coincide. Para
|
||||
/// mutar sin renderizar usa [`setup_component!`] y devuelve `None` explícitamente:
|
||||
/// Para renderizar usa [`render_component!`](crate::render_component), que devuelve `None` si
|
||||
/// ningún tipo coincide. Para mutar sin renderizar usa
|
||||
/// [`setup_component!`](crate::setup_component) y devuelve `None` explícitamente:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
|
|
@ -330,3 +331,106 @@ pub trait Theme: Extension + Send + Sync {
|
|||
|
||||
/// Referencia estática a un tema.
|
||||
pub type ThemeRef = &'static dyn Theme;
|
||||
|
||||
// **< render_component! >**************************************************************************
|
||||
|
||||
/// Sobrescribe el renderizado de componentes en [`Theme::handle_component()`].
|
||||
///
|
||||
/// Evalúa `$component` contra cada tipo de componente listado en orden. En cuanto encuentra
|
||||
/// coincidencia, devuelve `Some(Ok(markup))` o `Some(Err(e))` según el resultado de la expresión
|
||||
/// asociada. Si ningún tipo coincide, devuelve `None` para que el sistema continúe con la cadena de
|
||||
/// herencia o con el renderizado por defecto del propio componente.
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
/// &self,
|
||||
/// component: &dyn Component,
|
||||
/// cx: &mut Context,
|
||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||
/// render_component!(component, {
|
||||
/// Button => |btn| { Ok(html! { button.btn.btn-primary { (btn.label()) } }) },
|
||||
/// Heading => |h| self.render_heading(h, cx),
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// fn render_heading(&self, h: &Heading, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
/// Ok(html! { h2.display-4 { (h.text()) } })
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! render_component {
|
||||
($component:expr, { $($type:ty => |$var:ident| $body:expr),* $(,)? }) => {
|
||||
'render_component: {
|
||||
// Reborrow explícito como referencia compartida para que `downcast_ref` funcione
|
||||
// correctamente con `&mut dyn Component` (limitación del compilador con trait objects).
|
||||
let __c = &*($component);
|
||||
$(
|
||||
if let Some($var) = __c.downcast_ref::<$type>() {
|
||||
break 'render_component Some($body);
|
||||
}
|
||||
)*
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// **< setup_component! >***************************************************************************
|
||||
|
||||
/// Muta un componente dentro de [`Theme::handle_component()`].
|
||||
///
|
||||
/// Evalúa `$component` contra cada tipo de componente listado en orden. En cuanto encuentra
|
||||
/// coincidencia, ejecuta el bloque asociado y detiene la evaluación. Si ningún tipo coincide, no
|
||||
/// hace nada.
|
||||
///
|
||||
/// Usa acceso mutable al componente mediante [`downcast_mut`](crate::core::AnyCast::downcast_mut),
|
||||
/// lo que permite modificar su estado. El tema puede devolver `None` tras la mutación para que otro
|
||||
/// nivel de la cadena se encargue del renderizado.
|
||||
///
|
||||
/// # Ejemplos
|
||||
///
|
||||
/// Solo mutación: el tema ajusta el componente y delega el renderizado al siguiente nivel:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
/// &self,
|
||||
/// component: &mut dyn Component,
|
||||
/// cx: &mut Context,
|
||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||
/// setup_component!(component, { Button => |btn| { btn.add_class("btn-primary"); } });
|
||||
/// None
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Mutación y renderizado combinados: el `Button` se muta y se renderiza aquí; el `Heading` se
|
||||
/// muta pero continúa la cadena para que otro nivel lo renderice:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn handle_component(
|
||||
/// &self,
|
||||
/// component: &mut dyn Component,
|
||||
/// cx: &mut Context,
|
||||
/// ) -> Option<Result<Markup, ComponentError>> {
|
||||
/// setup_component!(component, {
|
||||
/// Button => |btn| { btn.add_class("btn-primary"); },
|
||||
/// Heading => |h| { h.add_class("display-4"); },
|
||||
/// });
|
||||
/// render_component!(component, {
|
||||
/// Button => |btn| Ok(html! { button.btn { (btn.label()) } }),
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! setup_component {
|
||||
($component:expr, { $($type:ty => |$var:ident| $body:expr),* $(,)? }) => {
|
||||
'setup_component: {
|
||||
$(
|
||||
if let Some($var) = ($component).downcast_mut::<$type>() {
|
||||
$body;
|
||||
break 'setup_component;
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
172
src/core/theme/layout.rs
Normal file
172
src/core/theme/layout.rs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
use crate::AutoDefault;
|
||||
use crate::core::AnyInfo;
|
||||
use crate::locale::Lc;
|
||||
|
||||
// **< RegionName >*********************************************************************************
|
||||
|
||||
/// Interfaz común para las regiones lógicas del `<body>`.
|
||||
///
|
||||
/// 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`]. 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 `<body>`, normalmente usando una plantilla
|
||||
/// ([`TemplateName`]) al renderizar la página ([`Page`]).
|
||||
///
|
||||
/// Requiere [`AnyInfo`] para que un [`RegionRef`] pueda recuperarse mediante
|
||||
/// [`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.
|
||||
///
|
||||
/// Este nombre es el identificador lógico de la región y se usa como clave en el
|
||||
/// [`Context`](crate::core::component::Context) para recuperar y renderizar el contenido
|
||||
/// registrado bajo ese nombre. Cualquier implementación de [`RegionName`] que devuelva el mismo
|
||||
/// nombre compartirá el mismo conjunto de componentes.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Devuelve un *texto localizado* como etiqueta de accesibilidad asociada a la región.
|
||||
///
|
||||
/// En la implementación predeterminada de [`Region`](crate::base::component::layout::Region),
|
||||
/// este valor se usa como `aria-label` del contenedor de la región.
|
||||
fn label(&self) -> Lc;
|
||||
}
|
||||
|
||||
/// Referencia estática a una región.
|
||||
pub type RegionRef = &'static dyn RegionName;
|
||||
|
||||
// **< CoreRegions >********************************************************************************
|
||||
|
||||
/// Regiones básicas que PageTop proporciona por defecto.
|
||||
///
|
||||
/// 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 [`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 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
|
||||
/// 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 RegionName for CoreRegions {
|
||||
#[inline]
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Header => "header",
|
||||
Self::Aside => "aside",
|
||||
Self::Content => "content",
|
||||
Self::Footer => "footer",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn label(&self) -> Lc {
|
||||
match self {
|
||||
Self::Header => Lc::l("region_header"),
|
||||
Self::Aside => Lc::l("region_aside"),
|
||||
Self::Content => Lc::l("region_content"),
|
||||
Self::Footer => Lc::l("region_footer"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **< TemplateName >*******************************************************************************
|
||||
|
||||
/// Interfaz común para las plantillas lógicas de una página.
|
||||
///
|
||||
/// Representa una variante identificada por un nombre. Un tema puede usar este nombre para decidir
|
||||
/// la composición del cuerpo de una página ([`Page`](crate::response::Page)), es decir, qué
|
||||
/// regiones ([`RegionName`]) renderizar y en qué orden.
|
||||
///
|
||||
/// Requiere [`AnyInfo`] por el mismo motivo que [`RegionName`], para que un [`TemplateRef`] 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 [`Template`](crate::base::component::layout::Template)).
|
||||
pub trait TemplateName: Send + Sync + AnyInfo {
|
||||
/// Devuelve el nombre de la plantilla.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Devuelve un *texto localizado* como etiqueta descriptiva de la plantilla.
|
||||
fn label(&self) -> Lc;
|
||||
}
|
||||
|
||||
/// Referencia estática a una plantilla.
|
||||
pub type TemplateRef = &'static dyn TemplateName;
|
||||
|
||||
// **< CoreTemplates >******************************************************************************
|
||||
|
||||
/// Plantillas que PageTop proporciona por defecto.
|
||||
#[derive(AutoDefault)]
|
||||
pub enum CoreTemplates {
|
||||
/// Plantilla predeterminada, de nombre `"standard"`.
|
||||
///
|
||||
/// Se emplea cuando no se selecciona ninguna otra plantilla explícitamente.
|
||||
#[default]
|
||||
Standard,
|
||||
|
||||
/// Plantilla para la **interfaz de administración**, de nombre `"admin"`.
|
||||
///
|
||||
/// Se utiliza para páginas de administración o paneles de control.
|
||||
Admin,
|
||||
}
|
||||
|
||||
impl TemplateName for CoreTemplates {
|
||||
#[inline]
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Standard => "standard",
|
||||
Self::Admin => "admin",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn label(&self) -> Lc {
|
||||
match self {
|
||||
Self::Standard => Lc::l("template-standard"),
|
||||
Self::Admin => Lc::l("template-admin"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue