🎨 Mejora Region para declarar las regiones

This commit is contained in:
Manuel Cillero 2025-10-04 08:25:04 +02:00
parent 9af2ac39a1
commit 4c8610af07
2 changed files with 27 additions and 17 deletions

View file

@ -28,14 +28,14 @@ pub type ThemeRef = &'static dyn Theme;
pub trait ThemePage { pub trait ThemePage {
/// Renderiza el **contenido interior** del `<body>` de la página. /// Renderiza el **contenido interior** del `<body>` de la página.
/// ///
/// Recorre `regions` en el **orden declarado** y, para cada región con contenido, genera un /// Esta implementación recorre `regions` en el **orden declarado** y, para cada región con
/// contenedor con `role="region"` y un `aria-label` localizado. /// contenido, genera un contenedor con `role="region"` y un `aria-label` localizado.
/// Se asume que cada identificador de región es **único** dentro de la página. /// Se asume que cada identificador de región es **único** dentro de la página.
/// ///
/// La etiqueta `<body>` no se incluye aquí; únicamente renderiza su contenido. /// La etiqueta `<body>` no se incluye aquí; únicamente renderiza su contenido.
fn render_body(&self, page: &mut Page, regions: &[(Region, L10n)]) -> Markup { fn render_body(&self, page: &mut Page, regions: &[Region]) -> Markup {
html! { html! {
@for (region, region_label) in regions { @for region in regions {
@let output = page.context().render_components_of(region.key()); @let output = page.context().render_components_of(region.key());
@if !output.is_empty() { @if !output.is_empty() {
@let region_name = region.name(); @let region_name = region.name();
@ -43,7 +43,7 @@ pub trait ThemePage {
id=(region_name) id=(region_name)
class={ "region region--" (region_name) } class={ "region region--" (region_name) }
role="region" role="region"
aria-label=[region_label.lookup(page)] aria-label=[region.label().lookup(page)]
{ {
(output) (output)
} }
@ -125,31 +125,31 @@ pub trait Theme: Extension + ThemePage + Send + Sync {
/// Declaración ordenada de las regiones disponibles en la página. /// Declaración ordenada de las regiones disponibles en la página.
/// ///
/// Devuelve una **lista estática** de pares `(Region, L10n)` que se usará para renderizar todas /// Devuelve una **lista estática** de regiones ([`Region`](crate::core::theme::Region)) con la
/// las regiones que componen una página en el orden indicado . /// información necesaria para renderizar el contenedor de cada región.
/// ///
/// Si un tema necesita un conjunto distinto de regiones, se puede **sobrescribir** este método /// Si un tema necesita un conjunto distinto de regiones, se puede **sobrescribir** este método
/// con los siguientes requisitos y recomendaciones: /// con los siguientes requisitos y recomendaciones:
/// ///
/// - Los identificadores deben ser **estables** (p. ej. `"sidebar-left"`, `"content"`). /// - Los identificadores deben ser **estables** (p. ej. `"sidebar-left"`, `"content"`).
/// - La región `"content"` es **obligatoria**. Se puede usar [`Region::default()`] para /// - La región `"content"` es **obligatoria** porque se usa por defecto para añadir componentes
/// declararla. /// para renderizar. Se puede utilizar [`Region::default()`] para declararla.
/// - La etiqueta `L10n` se evalúa con el idioma activo de la página. /// - La etiqueta `L10n` se evaluará con el idioma activo de la página.
/// ///
/// Por defecto devuelve: /// Por defecto devuelve:
/// ///
/// - `"header"`: cabecera. /// - `"header"`: cabecera.
/// - `"content"`: contenido principal (**obligatoria**). /// - `"content"`: contenido principal (**obligatoria**).
/// - `"footer"`: pie. /// - `"footer"`: pie.
fn page_regions(&self) -> &'static [(Region, L10n)] { fn page_regions(&self) -> &'static [Region] {
static REGIONS: LazyLock<[(Region, L10n); 3]> = LazyLock::new(|| { static REGIONS: LazyLock<[Region; 3]> = LazyLock::new(|| {
[ [
(Region::declare("header"), L10n::l("region_header")), Region::declare("header", L10n::l("region_header")),
(Region::default(), L10n::l("region_content")), Region::default(),
(Region::declare("footer"), L10n::l("region_footer")), Region::declare("footer", L10n::l("region_footer")),
] ]
}); });
&REGIONS[..] &*REGIONS
} }
/// Acciones específicas del tema antes de renderizar el `<body>` de la página. /// Acciones específicas del tema antes de renderizar el `<body>` de la página.

View file

@ -1,5 +1,6 @@
use crate::core::component::{Child, ChildOp, Children}; use crate::core::component::{Child, ChildOp, Children};
use crate::core::theme::ThemeRef; use crate::core::theme::ThemeRef;
use crate::locale::L10n;
use crate::{builder_fn, AutoDefault, UniqueId}; use crate::{builder_fn, AutoDefault, UniqueId};
use parking_lot::RwLock; use parking_lot::RwLock;
@ -29,6 +30,7 @@ pub const REGION_CONTENT: &str = "content";
pub struct Region { pub struct Region {
key: &'static str, key: &'static str,
name: String, name: String,
label: L10n,
} }
impl Default for Region { impl Default for Region {
@ -37,6 +39,7 @@ impl Default for Region {
Self { Self {
key: REGION_CONTENT, key: REGION_CONTENT,
name: REGION_CONTENT.to_string(), name: REGION_CONTENT.to_string(),
label: L10n::l("region_content"),
} }
} }
} }
@ -51,10 +54,11 @@ impl Region {
/// sencillos, limitando los caracteres a `[a-z0-9-]` (p.ej., `"sidebar"` o `"main-menu"`), cuyo /// sencillos, limitando los caracteres a `[a-z0-9-]` (p.ej., `"sidebar"` o `"main-menu"`), cuyo
/// nombre normalizado coincidirá con la clave. /// nombre normalizado coincidirá con la clave.
#[inline] #[inline]
pub fn declare(key: &'static str) -> Self { pub fn declare(key: &'static str, label: L10n) -> Self {
Self { Self {
key, key,
name: key.trim().to_ascii_lowercase().replace(' ', "-"), name: key.trim().to_ascii_lowercase().replace(' ', "-"),
label,
} }
} }
@ -69,6 +73,12 @@ impl Region {
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.name
} }
/// Devuelve la etiqueta localizada asociada a la región.
#[inline]
pub fn label(&self) -> &L10n {
&self.label
}
} }
// Contenedor interno de componentes agrupados por región. // Contenedor interno de componentes agrupados por región.