[pagetop] Añade macro Getters para estructuras

This commit is contained in:
Manuel Cillero 2025-11-30 12:51:45 +01:00
parent 0f76cfe28b
commit 4944af073f
17 changed files with 154 additions and 390 deletions

12
Cargo.lock generated
View file

@ -982,6 +982,17 @@ dependencies = [
"wasi 0.14.7+wasi-0.2.4",
]
[[package]]
name = "getter-methods"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43d815f896a3c730f0d76b8348a1700dc8d8fd6c377e4590d531bdd646574d8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ghash"
version = "0.5.1"
@ -1558,6 +1569,7 @@ dependencies = [
"config",
"figlet-rs",
"fluent-templates",
"getter-methods",
"indoc",
"itoa",
"pagetop-aliner",

View file

@ -20,6 +20,7 @@ colored = "3.0"
concat-string = "1.0"
config = { version = "0.15", default-features = false, features = ["toml"] }
figlet-rs = "0.1"
getter-methods = "2.0"
indoc = "2.0"
itoa = "1.0"
parking_lot = "0.12"

View file

@ -6,14 +6,18 @@ use crate::prelude::*;
///
/// Envuelve un contenido con la etiqueta HTML indicada por [`container::Kind`]. Sólo se renderiza
/// si existen componentes hijos (*children*).
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Container {
id : AttrId,
classes : AttrClasses,
container_kind : container::Kind,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al contenedor.
classes: AttrClasses,
/// Devuelve el tipo semántico del contenedor.
container_kind: container::Kind,
/// Devuelve el comportamiento para el ancho del contenedor.
container_width: container::Width,
children : Children,
/// Devuelve la lista de componentes (`children`) del contenedor.
children: Children,
}
impl Component for Container {
@ -26,7 +30,7 @@ impl Component for Container {
}
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.alter_classes(ClassesOp::Prepend, self.width().to_class());
self.alter_classes(ClassesOp::Prepend, self.container_width().to_class());
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
@ -34,7 +38,7 @@ impl Component for Container {
if output.is_empty() {
return PrepareMarkup::None;
}
let style = match self.width() {
let style = match self.container_width() {
container::Width::FluidMax(w) if w.is_measurable() => {
Some(join!("max-width: ", w.to_string(), ";"))
}
@ -159,26 +163,4 @@ impl Container {
self.children.alter_child(op);
self
}
// **< Container GETTERS >**********************************************************************
/// Devuelve las clases CSS asociadas al contenedor.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el tipo semántico del contenedor.
pub fn container_kind(&self) -> &container::Kind {
&self.container_kind
}
/// Devuelve el comportamiento para el ancho del contenedor.
pub fn width(&self) -> &container::Width {
&self.container_width
}
/// Devuelve la lista de componentes (`children`) del contenedor.
pub fn children(&self) -> &Children {
&self.children
}
}

View file

@ -19,21 +19,32 @@ use crate::LOCALES_BOOTSIER;
///
/// Ver ejemplo en el módulo [`dropdown`].
/// Si no contiene elementos, el componente **no se renderiza**.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Dropdown {
id : AttrId,
classes : AttrClasses,
title : L10n,
button_size : ButtonSize,
button_color : ButtonColor,
button_split : bool,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al menú desplegable.
classes: AttrClasses,
/// Devuelve el título del menú desplegable.
title: L10n,
/// Devuelve el tamaño configurado del botón.
button_size: ButtonSize,
/// Devuelve el color/estilo configurado del botón.
button_color: ButtonColor,
/// Devuelve si se debe desdoblar (*split*) el botón (botón de acción + *toggle*).
button_split: bool,
/// Devuelve si el botón del menú está integrado en un grupo de botones.
button_grouped: bool,
auto_close : dropdown::AutoClose,
direction : dropdown::Direction,
menu_align : dropdown::MenuAlign,
menu_position : dropdown::MenuPosition,
items : Children,
/// Devuelve la política de cierre automático del menú desplegado.
auto_close: dropdown::AutoClose,
/// Devuelve la dirección de despliegue configurada.
direction: dropdown::Direction,
/// Devuelve la configuración de alineación horizontal del menú desplegable.
menu_align: dropdown::MenuAlign,
/// Devuelve la posición configurada para el menú desplegable.
menu_position: dropdown::MenuPosition,
/// Devuelve la lista de elementos del menú.
items: Children,
}
impl Component for Dropdown {
@ -48,7 +59,7 @@ impl Component for Dropdown {
fn setup_before_prepare(&mut self, _cx: &mut Context) {
self.alter_classes(
ClassesOp::Prepend,
self.direction().class_with(self.button_grouped()),
self.direction().class_with(*self.button_grouped()),
);
}
@ -82,7 +93,7 @@ impl Component for Dropdown {
});
// Renderizado en modo split (dos botones) o simple (un botón).
@if self.button_split() {
@if *self.button_split() {
// Botón principal (acción/etiqueta).
@let btn = html! {
button
@ -242,61 +253,4 @@ impl Dropdown {
self.items.alter_typed(op);
self
}
// **< Dropdown GETTERS >***********************************************************************
/// Devuelve las clases CSS asociadas al menú desplegable.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el título del menú desplegable.
pub fn title(&self) -> &L10n {
&self.title
}
/// Devuelve el tamaño configurado del botón.
pub fn button_size(&self) -> &ButtonSize {
&self.button_size
}
/// Devuelve el color/estilo configurado del botón.
pub fn button_color(&self) -> &ButtonColor {
&self.button_color
}
/// Devuelve si se debe desdoblar (*split*) el botón (botón de acción + *toggle*).
pub fn button_split(&self) -> bool {
self.button_split
}
/// Devuelve si el botón del menú está integrado en un grupo de botones.
pub fn button_grouped(&self) -> bool {
self.button_grouped
}
/// Devuelve la política de cierre automático del menú desplegado.
pub fn auto_close(&self) -> &dropdown::AutoClose {
&self.auto_close
}
/// Devuelve la dirección de despliegue configurada.
pub fn direction(&self) -> &dropdown::Direction {
&self.direction
}
/// Devuelve la configuración de alineación horizontal del menú desplegable.
pub fn menu_align(&self) -> &dropdown::MenuAlign {
&self.menu_align
}
/// Devuelve la posición configurada para el menú desplegable.
pub fn menu_position(&self) -> &dropdown::MenuPosition {
&self.menu_position
}
/// Devuelve la lista de elementos (`children`) del menú.
pub fn items(&self) -> &Children {
&self.items
}
}

View file

@ -42,11 +42,13 @@ pub enum ItemKind {
///
/// Permite definir identificador, clases de estilo adicionales o tipo de interacción asociada,
/// manteniendo una interfaz común para renderizar todos los elementos del menú.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Item {
id : AttrId,
classes : AttrClasses,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al elemento.
classes: AttrClasses,
/// Devuelve el tipo de elemento representado.
item_kind: ItemKind,
}
@ -266,16 +268,4 @@ impl Item {
self.classes.alter_value(op, classes);
self
}
// **< Item GETTERS >***************************************************************************
/// Devuelve las clases CSS asociadas al elemento.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el tipo de elemento representado.
pub fn item_kind(&self) -> &ItemKind {
&self.item_kind
}
}

View file

@ -13,11 +13,11 @@ pub enum IconKind {
},
}
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Icon {
classes : AttrClasses,
icon_kind : IconKind,
/// Devuelve las clases CSS asociadas al icono.
classes: AttrClasses,
icon_kind: IconKind,
aria_label: AttrL10n,
}
@ -116,19 +116,4 @@ impl Icon {
self.aria_label.alter_value(label);
self
}
// **< Icon GETTERS >***************************************************************************
/// Devuelve las clases CSS asociadas al icono.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
pub fn icon_kind(&self) -> &IconKind {
&self.icon_kind
}
pub fn aria_label(&self) -> &AttrL10n {
&self.aria_label
}
}

View file

@ -9,14 +9,18 @@ use crate::prelude::*;
/// ([`classes::Border`](crate::theme::classes::Border)) y **redondeo de esquinas**
/// ([`classes::Rounded`](crate::theme::classes::Rounded)).
/// - Resuelve el texto alternativo `alt` con **localización** mediante [`L10n`].
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Image {
id : AttrId,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas a la imagen.
classes: AttrClasses,
size : image::Size,
source : image::Source,
alt : AttrL10n,
/// Devuelve las dimensiones de la imagen.
size: image::Size,
/// Devuelve el origen de la imagen.
source: image::Source,
/// Devuelve el texto alternativo localizado.
alternative: AttrL10n,
}
impl Component for Image {
@ -113,29 +117,7 @@ impl Image {
/// decorativa.
#[builder_fn]
pub fn with_alternative(mut self, alt: L10n) -> Self {
self.alt.alter_value(alt);
self.alternative.alter_value(alt);
self
}
// **< Image GETTERS >**************************************************************************
/// Devuelve las clases CSS asociadas a la imagen.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve las dimensiones de la imagen.
pub fn size(&self) -> &image::Size {
&self.size
}
/// Devuelve el origen de la imagen.
pub fn source(&self) -> &image::Source {
&self.source
}
/// Devuelve el texto alternativo localizado.
pub fn alternative(&self) -> &AttrL10n {
&self.alt
}
}

View file

@ -10,14 +10,18 @@ use crate::prelude::*;
///
/// Ver ejemplo en el módulo [`nav`].
/// Si no contiene elementos, el componente **no se renderiza**.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Nav {
id : AttrId,
classes : AttrClasses,
items : Children,
nav_kind : nav::Kind,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al menú.
classes: AttrClasses,
/// Devuelve el estilo visual seleccionado.
nav_kind: nav::Kind,
/// Devuelve la distribución y orientación seleccionada.
nav_layout: nav::Layout,
/// Devuelve la lista de elementos del menú.
items: Children,
}
impl Component for Nav {
@ -110,26 +114,4 @@ impl Nav {
self.items.alter_typed(op);
self
}
// **< Nav GETTERS >****************************************************************************
/// Devuelve las clases CSS asociadas al menú.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el estilo visual seleccionado.
pub fn nav_kind(&self) -> &nav::Kind {
&self.nav_kind
}
/// Devuelve la distribución y orientación seleccionada.
pub fn nav_layout(&self) -> &nav::Layout {
&self.nav_layout
}
/// Devuelve la lista de elementos (`children`) del menú.
pub fn items(&self) -> &Children {
&self.items
}
}

View file

@ -72,11 +72,13 @@ impl ItemKind {
///
/// Permite definir identificador, clases de estilo adicionales o tipo de interacción asociada,
/// manteniendo una interfaz común para renderizar todos los elementos del menú.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Item {
id : AttrId,
classes : AttrClasses,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al elemento.
classes: AttrClasses,
/// Devuelve el tipo de elemento representado.
item_kind: ItemKind,
}
@ -269,16 +271,4 @@ impl Item {
self.classes.alter_value(op, classes);
self
}
// **< Item GETTERS >***************************************************************************
/// Devuelve las clases CSS asociadas al elemento.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el tipo de elemento representado.
pub fn item_kind(&self) -> &ItemKind {
&self.item_kind
}
}

View file

@ -11,16 +11,20 @@ use crate::prelude::*;
/// - Si no hay imagen ([`with_image()`](Self::with_image)) ni título
/// ([`with_title()`](Self::with_title)), la marca de identidad no se renderiza.
/// - El eslogan ([`with_slogan()`](Self::with_slogan)) es opcional; por defecto no tiene contenido.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Brand {
id : AttrId,
image : Typed<Image>,
#[getters(skip)]
id: AttrId,
/// Devuelve la imagen de marca (si la hay).
image: Typed<Image>,
/// Devuelve el título de la identidad de marca.
#[default(_code = "L10n::n(&global::SETTINGS.app.name)")]
title : L10n,
title: L10n,
/// Devuelve el eslogan de la marca.
slogan: L10n,
/// Devuelve la función que resuelve la URL asociada a la marca (si existe).
#[default(_code = "Some(|_| \"/\")")]
path : Option<FnPathByContext>,
path: Option<FnPathByContext>,
}
impl Component for Brand {
@ -86,26 +90,4 @@ impl Brand {
self.path = path;
self
}
// **< Brand GETTERS >**************************************************************************
/// Devuelve la imagen de marca (si la hay).
pub fn image(&self) -> &Typed<Image> {
&self.image
}
/// Devuelve el título de la identidad de marca.
pub fn title(&self) -> &L10n {
&self.title
}
/// Devuelve el eslogan de la marca.
pub fn slogan(&self) -> &L10n {
&self.slogan
}
/// Devuelve la función que resuelve la URL asociada a la marca (si existe).
pub fn path(&self) -> &Option<FnPathByContext> {
&self.path
}
}

View file

@ -14,15 +14,20 @@ const TOGGLE_OFFCANVAS: &str = "offcanvas";
///
/// Ver ejemplos en el módulo [`navbar`].
/// Si no contiene elementos, el componente **no se renderiza**.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Navbar {
id : AttrId,
classes : AttrClasses,
expand : BreakPoint,
layout : navbar::Layout,
position : navbar::Position,
items : Children,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas a la barra de navegación.
classes: AttrClasses,
/// Devuelve el punto de ruptura configurado.
expand: BreakPoint,
/// Devuelve la disposición configurada para la barra de navegación.
layout: navbar::Layout,
/// Devuelve la posición configurada para la barra de navegación.
position: navbar::Position,
/// Devuelve la lista de contenidos.
items: Children,
}
impl Component for Navbar {
@ -263,31 +268,4 @@ impl Navbar {
self.items.alter_typed(op);
self
}
// **< Navbar GETTERS >*************************************************************************
/// Devuelve las clases CSS asociadas a la barra de navegación.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el punto de ruptura configurado.
pub fn expand(&self) -> &BreakPoint {
&self.expand
}
/// Devuelve la disposición configurada para la barra de navegación.
pub fn layout(&self) -> &navbar::Layout {
&self.layout
}
/// Devuelve la posición configurada para la barra de navegación.
pub fn position(&self) -> &navbar::Position {
&self.position
}
/// Devuelve la lista de contenidos (`children`).
pub fn items(&self) -> &Children {
&self.items
}
}

View file

@ -21,18 +21,26 @@ use crate::LOCALES_BOOTSIER;
///
/// Ver ejemplo en el módulo [`offcanvas`].
/// Si no contiene elementos, el componente **no se renderiza**.
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Offcanvas {
id : AttrId,
classes : AttrClasses,
title : L10n,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al panel.
classes: AttrClasses,
/// Devuelve el título del panel.
title: L10n,
/// Devuelve el punto de ruptura configurado para cambiar el comportamiento del panel.
breakpoint: BreakPoint,
backdrop : offcanvas::Backdrop,
scrolling : offcanvas::BodyScroll,
placement : offcanvas::Placement,
/// Devuelve el comportamiento configurado para la capa de fondo.
backdrop: offcanvas::Backdrop,
/// Indica si la página principal puede desplazarse mientras el panel está abierto.
body_scroll: offcanvas::BodyScroll,
/// Devuelve la posición de inicio del panel.
placement: offcanvas::Placement,
/// Devuelve el estado inicial del panel.
visibility: offcanvas::Visibility,
children : Children,
/// Devuelve la lista de componentes (`children`) del panel.
children: Children,
}
impl Component for Offcanvas {
@ -109,7 +117,7 @@ impl Offcanvas {
/// Permite o bloquea el desplazamiento de la página principal mientras el panel está abierto.
#[builder_fn]
pub fn with_body_scroll(mut self, scrolling: offcanvas::BodyScroll) -> Self {
self.scrolling = scrolling;
self.body_scroll = scrolling;
self
}
@ -141,48 +149,6 @@ impl Offcanvas {
self
}
// **< Offcanvas GETTERS >**********************************************************************
/// Devuelve las clases CSS asociadas al panel.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el título del panel.
pub fn title(&self) -> &L10n {
&self.title
}
/// Devuelve el punto de ruptura configurado para cambiar el comportamiento del panel.
pub fn breakpoint(&self) -> &BreakPoint {
&self.breakpoint
}
/// Devuelve el comportamiento configurado para la capa de fondo.
pub fn backdrop(&self) -> &offcanvas::Backdrop {
&self.backdrop
}
/// Indica si la página principal puede desplazarse mientras el panel está abierto.
pub fn body_scroll(&self) -> &offcanvas::BodyScroll {
&self.scrolling
}
/// Devuelve la posición de inicio del panel.
pub fn placement(&self) -> &offcanvas::Placement {
&self.placement
}
/// Devuelve el estado inicial del panel.
pub fn visibility(&self) -> &offcanvas::Visibility {
&self.visibility
}
/// Devuelve la lista de componentes (`children`) del panel.
pub fn children(&self) -> &Children {
&self.children
}
// **< Offcanvas HELPERS >**********************************************************************
pub(crate) fn render_offcanvas(&self, cx: &mut Context, extra: Option<&Children>) -> Markup {

View file

@ -4,12 +4,15 @@ use crate::prelude::*;
///
/// Los bloques se utilizan como contenedores de otros componentes o contenidos, con un título
/// opcional y un cuerpo que sólo se renderiza si existen componentes hijos (*children*).
#[rustfmt::skip]
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct Block {
id : AttrId,
classes : AttrClasses,
title : L10n,
#[getters(skip)]
id: AttrId,
/// Devuelve las clases CSS asociadas al bloque.
classes: AttrClasses,
/// Devuelve el título del bloque.
title: L10n,
/// Devuelve la lista de componentes hijo del bloque.
children: Children,
}
@ -83,21 +86,4 @@ impl Block {
self.children.alter_child(op);
self
}
// **< Block GETTERS >**************************************************************************
/// Devuelve las clases CSS asociadas al bloque.
pub fn classes(&self) -> &AttrClasses {
&self.classes
}
/// Devuelve el título del bloque.
pub fn title(&self) -> &L10n {
&self.title
}
/// Devuelve la lista de componentes (`children`) del bloque.
pub fn children(&self) -> &Children {
&self.children
}
}

View file

@ -76,12 +76,17 @@ pub enum IntroOpening {
/// })),
/// );
/// ```
#[rustfmt::skip]
#[derive(Getters)]
pub struct Intro {
title : L10n,
slogan : L10n,
button : Option<(L10n, FnPathByContext)>,
opening : IntroOpening,
/// Devuelve el título de entrada.
title: L10n,
/// Devuelve el eslogan de la entrada.
slogan: L10n,
/// Devuelve el botón de llamada a la acción, si existe.
button: Option<(L10n, FnPathByContext)>,
/// Devuelve el modo de apertura configurado.
opening: IntroOpening,
/// Devuelve la lista de componentes hijo de la intro.
children: Children,
}
@ -110,7 +115,7 @@ impl Component for Intro {
}
fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup {
if self.opening() == IntroOpening::PageTop {
if *self.opening() == IntroOpening::PageTop {
cx.alter_assets(ContextOp::AddJavaScript(JavaScript::on_load_async("intro-js", |cx|
util::indoc!(r#"
try {
@ -163,7 +168,7 @@ impl Component for Intro {
}
}
div class="intro-text__children" {
@if self.opening() == IntroOpening::PageTop {
@if *self.opening() == IntroOpening::PageTop {
p { (L10n::l("intro_text1").using(cx)) }
div id="intro-badges" {
img
@ -289,31 +294,4 @@ impl Intro {
self.children.alter_child(op);
self
}
// **< Intro GETTERS >**************************************************************************
/// Devuelve el título de entrada.
pub fn title(&self) -> &L10n {
&self.title
}
/// Devuelve el eslogan de la entrada.
pub fn slogan(&self) -> &L10n {
&self.slogan
}
/// Devuelve el botón de llamada a la acción, si existe.
pub fn button(&self) -> Option<(&L10n, &FnPathByContext)> {
self.button.as_ref().map(|(txt, lnk)| (txt, lnk))
}
/// Devuelve el modo de apertura configurado.
pub fn opening(&self) -> IntroOpening {
self.opening
}
/// Devuelve la lista de componentes (`children`) de la intro.
pub fn children(&self) -> &Children {
&self.children
}
}

View file

@ -8,8 +8,9 @@ const LINK: &str = "<a href=\"https://pagetop.cillero.es\" rel=\"noopener norefe
/// Por defecto, usando [`default()`](Self::default) sólo se muestra un reconocimiento a PageTop.
/// Sin embargo, se puede usar [`new()`](Self::new) para crear una instancia con un texto de
/// copyright predeterminado.
#[derive(AutoDefault)]
#[derive(AutoDefault, Getters)]
pub struct PoweredBy {
/// Devuelve el texto de copyright actual, si existe.
copyright: Option<String>,
}
@ -56,11 +57,4 @@ impl PoweredBy {
self.copyright = copyright.map(Into::into);
self
}
// **< PoweredBy GETTERS >**********************************************************************
/// Devuelve el texto de copyright actual, si existe.
pub fn copyright(&self) -> Option<&str> {
self.copyright.as_deref()
}
}

View file

@ -137,6 +137,8 @@ pub use pagetop_macros::{builder_fn, html, main, test, AutoDefault};
pub use pagetop_statics::{resource, StaticResource};
pub use getter_methods::Getters;
/// Contenedor para un conjunto de recursos embebidos.
#[derive(AutoDefault)]
pub struct StaticResources {

View file

@ -6,7 +6,7 @@ pub use crate::PAGETOP_VERSION;
pub use crate::{builder_fn, html, main, test};
pub use crate::{AutoDefault, StaticResources, UniqueId, Weight};
pub use crate::{AutoDefault, Getters, StaticResources, UniqueId, Weight};
// MACROS.