diff --git a/extensions/pagetop-bootsier/src/theme.rs b/extensions/pagetop-bootsier/src/theme.rs index b5be6b7..ece34f0 100644 --- a/extensions/pagetop-bootsier/src/theme.rs +++ b/extensions/pagetop-bootsier/src/theme.rs @@ -12,8 +12,9 @@ pub mod dropdown; pub use dropdown::Dropdown; // Image. -mod image; -pub use image::{Image, ImageSize}; +pub mod image; +#[doc(inline)] +pub use image::Image; // Navbar. pub mod navbar; diff --git a/extensions/pagetop-bootsier/src/theme/container.rs b/extensions/pagetop-bootsier/src/theme/container.rs index 22f7fc6..1751250 100644 --- a/extensions/pagetop-bootsier/src/theme/container.rs +++ b/extensions/pagetop-bootsier/src/theme/container.rs @@ -234,7 +234,7 @@ impl Container { self } - /// Establece esquinas redondeadas para el contenedor. + /// Establece esquinas redondeadas para el contenedor ([`Rounded`]). #[builder_fn] pub fn with_rounded(mut self, rounded: Rounded) -> Self { self.rounded = rounded; @@ -282,12 +282,12 @@ impl Container { &self.text_color } - /// Devuelve el borde del contenedor. + /// Devuelve el borde configurado del contenedor. pub fn border(&self) -> &Border { &self.border } - /// Devuelve las esquinas redondeadas del contenedor. + /// Devuelve las esquinas redondeadas configuradas para el contenedor. pub fn rounded(&self) -> &Rounded { &self.rounded } diff --git a/extensions/pagetop-bootsier/src/theme/image.rs b/extensions/pagetop-bootsier/src/theme/image.rs index 6c60278..837d25a 100644 --- a/extensions/pagetop-bootsier/src/theme/image.rs +++ b/extensions/pagetop-bootsier/src/theme/image.rs @@ -1,186 +1,7 @@ -use pagetop::prelude::*; +//! Definiciones para renderizar imágenes ([`Image`]). -use crate::prelude::*; +mod props; +pub use props::{Size, Source}; -#[derive(AutoDefault)] -pub enum ImageSource { - #[default] - //Logo(PageTopLogo), - Responsive(String), - Thumbnail(String), - Static(String), -} - -#[derive(AutoDefault)] -pub enum ImageSize { - #[default] - Auto, - Dimensions(UnitValue, UnitValue), - Width(UnitValue), - Height(UnitValue), - Both(UnitValue), -} - -#[rustfmt::skip] -#[derive(AutoDefault)] -pub struct Image { - id : AttrId, - classes: AttrClasses, - source : ImageSource, - alt : AttrL10n, - size : ImageSize, - border : Border, - rounded: Rounded, -} - -impl Component for Image { - fn new() -> Self { - Image::default() - } - - fn id(&self) -> Option { - self.id.get() - } - - fn setup_before_prepare(&mut self, _cx: &mut Context) { - self.alter_classes( - ClassesOp::Prepend, - [ - String::from(match self.source() { - //ImageSource::Logo(_) => "img-fluid", - ImageSource::Responsive(_) => "img-fluid", - ImageSource::Thumbnail(_) => "img-thumbnail", - _ => "", - }), - self.border().to_string(), - self.rounded().to_string(), - ] - .join(" "), - ); - } - - fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { - let dimensions = match self.size() { - ImageSize::Auto => None, - ImageSize::Dimensions(w, h) => { - let w = w.to_string(); - let h = h.to_string(); - Some(join!("width: ", w, "; height: ", h, ";")) - } - ImageSize::Width(w) => { - let w = w.to_string(); - Some(join!("width: ", w, ";")) - } - ImageSize::Height(h) => { - let h = h.to_string(); - Some(join!("height: ", h, ";")) - } - ImageSize::Both(v) => { - let v = v.to_string(); - Some(join!("width: ", v, "; height: ", v, ";")) - } - }; - let source = match self.source() { - /* - ImageSource::Logo(logo) => { - return PrepareMarkup::With(html! { - span - id=[self.id()] - class=[self.classes().get()] - style=[dimensions] - { - (logo.render(cx)) - } - }) - } - */ - ImageSource::Responsive(source) => Some(source), - ImageSource::Thumbnail(source) => Some(source), - ImageSource::Static(source) => Some(source), - }; - PrepareMarkup::With(html! { - img - src=[source] - alt=[self.alternative().lookup(cx)] - id=[self.id()] - class=[self.classes().get()] - style=[dimensions] {} - }) - } -} - -impl Image { - pub fn with(source: ImageSource) -> Self { - Image::default().with_source(source) - } - - // **< Image BUILDER >************************************************************************** - - #[builder_fn] - pub fn with_id(mut self, id: impl AsRef) -> Self { - self.id.alter_value(id); - self - } - - #[builder_fn] - pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef) -> Self { - self.classes.alter_value(op, classes); - self - } - - #[builder_fn] - pub fn with_source(mut self, source: ImageSource) -> Self { - self.source = source; - self - } - - #[builder_fn] - pub fn with_alternative(mut self, alt: L10n) -> Self { - self.alt.alter_value(alt); - self - } - - #[builder_fn] - pub fn with_size(mut self, size: ImageSize) -> Self { - self.size = size; - self - } - - #[builder_fn] - pub fn with_border(mut self, border: Border) -> Self { - self.border = border; - self - } - - #[builder_fn] - pub fn with_rounded(mut self, rounded: Rounded) -> Self { - self.rounded = rounded; - self - } - - // **< Image GETTERS >************************************************************************** - - pub fn classes(&self) -> &AttrClasses { - &self.classes - } - - pub fn source(&self) -> &ImageSource { - &self.source - } - - pub fn alternative(&self) -> &AttrL10n { - &self.alt - } - - pub fn size(&self) -> &ImageSize { - &self.size - } - - pub fn border(&self) -> &Border { - &self.border - } - - pub fn rounded(&self) -> &Rounded { - &self.rounded - } -} +mod component; +pub use component::Image; diff --git a/extensions/pagetop-bootsier/src/theme/image/component.rs b/extensions/pagetop-bootsier/src/theme/image/component.rs new file mode 100644 index 0000000..0345adb --- /dev/null +++ b/extensions/pagetop-bootsier/src/theme/image/component.rs @@ -0,0 +1,194 @@ +use pagetop::prelude::*; + +use crate::prelude::*; + +/// Componente para renderizar una **imagen**. +/// +/// - Ajusta su disposición según el origen definido en [`image::Source`]. +/// - Permite configurar **dimensiones** ([`with_size()`](Self::with_size)), **borde** +/// ([`with_border()`](Self::with_border)) y **redondeo de esquinas** +/// ([`with_rounded()`](Self::with_rounded)). +/// - Resuelve el texto alternativo `alt` con **localización** mediante [`L10n`]. +#[rustfmt::skip] +#[derive(AutoDefault)] +pub struct Image { + id : AttrId, + classes: AttrClasses, + size : image::Size, + source : image::Source, + alt : AttrL10n, + border : Border, + rounded: Rounded, +} + +impl Component for Image { + fn new() -> Self { + Image::default() + } + + fn id(&self) -> Option { + self.id.get() + } + + fn setup_before_prepare(&mut self, _cx: &mut Context) { + self.alter_classes( + ClassesOp::Prepend, + [ + String::from(match self.source() { + image::Source::Logo(_) => "img-fluid", + image::Source::Responsive(_) => "img-fluid", + image::Source::Thumbnail(_) => "img-thumbnail", + image::Source::Plain(_) => "", + }), + self.border().to_string(), + self.rounded().to_string(), + ] + .join(" "), + ); + } + + fn prepare_component(&self, cx: &mut Context) -> PrepareMarkup { + let dimensions = match self.size() { + image::Size::Auto => None, + image::Size::Dimensions(w, h) => { + let w = w.to_string(); + let h = h.to_string(); + Some(join!("width: ", w, "; height: ", h, ";")) + } + image::Size::Width(w) => { + let w = w.to_string(); + Some(join!("width: ", w, ";")) + } + image::Size::Height(h) => { + let h = h.to_string(); + Some(join!("height: ", h, ";")) + } + image::Size::Both(v) => { + let v = v.to_string(); + Some(join!("width: ", v, "; height: ", v, ";")) + } + }; + let alt_text = self.alternative().lookup(cx).unwrap_or_default(); + let is_decorative = alt_text.is_empty(); + let source = match self.source() { + image::Source::Logo(logo) => { + return PrepareMarkup::With(html! { + span + id=[self.id()] + class=[self.classes().get()] + style=[dimensions] + role=[(!is_decorative).then_some("img")] + aria-label=[(!is_decorative).then_some(alt_text)] + aria-hidden=[is_decorative.then_some("true")] + { + (logo.render(cx)) + } + }) + } + image::Source::Responsive(source) => Some(source), + image::Source::Thumbnail(source) => Some(source), + image::Source::Plain(source) => Some(source), + }; + PrepareMarkup::With(html! { + img + src=[source] + alt=(alt_text) + id=[self.id()] + class=[self.classes().get()] + style=[dimensions] {} + }) + } +} + +impl Image { + /// Crea rápidamente una imagen especificando su origen. + pub fn with(source: image::Source) -> Self { + Image::default().with_source(source) + } + + // **< Image BUILDER >************************************************************************** + + /// Establece el identificador único (`id`) de la imagen. + #[builder_fn] + pub fn with_id(mut self, id: impl AsRef) -> Self { + self.id.alter_value(id); + self + } + + /// Modifica la lista de clases CSS aplicadas a la imagen. + #[builder_fn] + pub fn with_classes(mut self, op: ClassesOp, classes: impl AsRef) -> Self { + self.classes.alter_value(op, classes); + self + } + + /// Define las dimensiones de la imagen (auto, ancho/alto, ambos). + #[builder_fn] + pub fn with_size(mut self, size: image::Size) -> Self { + self.size = size; + self + } + + /// Establece el origen de la imagen, influyendo en su disposición en el contenido. + #[builder_fn] + pub fn with_source(mut self, source: image::Source) -> Self { + self.source = source; + self + } + + /// Define el texto alternativo localizado ([`L10n`]) para la imagen. + /// + /// Se recomienda siempre aportar un texto alternativo salvo que la imagen sea puramente + /// decorativa. + #[builder_fn] + pub fn with_alternative(mut self, alt: L10n) -> Self { + self.alt.alter_value(alt); + self + } + + /// Establece el borde de la imagen ([`Border`]). + #[builder_fn] + pub fn with_border(mut self, border: Border) -> Self { + self.border = border; + self + } + + /// Establece esquinas redondeadas para la imagen ([`Rounded`]). + #[builder_fn] + pub fn with_rounded(mut self, rounded: Rounded) -> Self { + self.rounded = rounded; + 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 + } + + /// Devuelve el borde configurado de la imagen. + pub fn border(&self) -> &Border { + &self.border + } + + /// Devuelve las esquinas redondeadas configuradas para la imagen. + pub fn rounded(&self) -> &Rounded { + &self.rounded + } +} diff --git a/extensions/pagetop-bootsier/src/theme/image/props.rs b/extensions/pagetop-bootsier/src/theme/image/props.rs new file mode 100644 index 0000000..f871de7 --- /dev/null +++ b/extensions/pagetop-bootsier/src/theme/image/props.rs @@ -0,0 +1,53 @@ +use pagetop::prelude::*; + +// **< Size >*************************************************************************************** + +/// Define las **dimensiones** de una imagen ([`Image`](crate::theme::Image)). +#[derive(AutoDefault)] +pub enum Size { + /// Ajuste automático por defecto. + /// + /// La imagen usa su tamaño natural o se ajusta al contenedor donde se publica. + #[default] + Auto, + /// Establece explícitamente el **ancho y alto** de la imagen. + /// + /// Útil cuando se desea fijar ambas dimensiones de forma exacta. Ten en cuenta que la imagen + /// puede distorsionarse si no se mantiene la proporción original. + Dimensions(UnitValue, UnitValue), + /// Establece sólo el **ancho** de la imagen. + /// + /// La altura se ajusta proporcionalmente de manera automática. + Width(UnitValue), + /// Establece sólo la **altura** de la imagen. + /// + /// El ancho se ajusta proporcionalmente de manera automática. + Height(UnitValue), + /// Establece **el mismo valor** para el ancho y el alto de la imagen. + /// + /// Práctico para forzar rápidamente un área cuadrada. Ten en cuenta que la imagen puede + /// distorsionarse si la original no es cuadrada. + Both(UnitValue), +} + +// **< Source >************************************************************************************* + +/// Especifica la **fuente** para publicar una imagen ([`Image`](crate::theme::Image)). +#[derive(AutoDefault)] +pub enum Source { + /// Imagen con el logotipo de PageTop. + #[default] + Logo(PageTopSvg), + /// Imagen que se adapta automáticamente a su contenedor. + /// + /// El `String` asociado es la URL (o ruta) de la imagen. + Responsive(String), + /// Imagen que aplica el estilo **miniatura** de Bootstrap. + /// + /// El `String` asociado es la URL (o ruta) de la imagen. + Thumbnail(String), + /// Imagen sin clases específicas de Bootstrap, útil para controlar con CSS propio. + /// + /// El `String` asociado es la URL (o ruta) de la imagen. + Plain(String), +}