♻️ (bootsier): Mueve Image al core de PageTop
`Image`, `Size` y `Source` pasan a `pagetop::base::component` como componente genérico reutilizable por cualquier tema. Bootsier sustituye las clases semánticas por las de Bootstrap (`img-fluid`, `img-thumbnail`) interceptando `setup()`.
This commit is contained in:
parent
745d492d5b
commit
c3ff8a6ff8
6 changed files with 97 additions and 88 deletions
|
|
@ -30,6 +30,10 @@ pub use form::Form;
|
|||
mod html;
|
||||
pub use html::Html;
|
||||
|
||||
pub mod image;
|
||||
#[doc(inline)]
|
||||
pub use image::Image;
|
||||
|
||||
mod intro;
|
||||
pub use intro::{Intro, IntroOpening};
|
||||
|
||||
|
|
|
|||
7
src/base/component/image.rs
Normal file
7
src/base/component/image.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//! Definiciones para renderizar imágenes ([`Image`]).
|
||||
|
||||
mod props;
|
||||
pub use props::{Size, Source};
|
||||
|
||||
mod component;
|
||||
pub use component::Image;
|
||||
146
src/base/component/image/component.rs
Normal file
146
src/base/component/image/component.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
/// Componente para renderizar una **imagen**.
|
||||
///
|
||||
/// A una imagen se le puede:
|
||||
///
|
||||
/// - Establecer su contenido a partir del origen definido en [`image::Source`].
|
||||
/// - Configurar sus **dimensiones** ([`with_size()`](Self::with_size)).
|
||||
/// - Aplicar el texto alternativo `alt` con **localización** mediante [`Lc`].
|
||||
///
|
||||
/// # Ejemplo
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use pagetop::prelude::*;
|
||||
///
|
||||
/// let logo = Image::with(image::Source::logo(PageTopSvg::Color))
|
||||
/// .with_alternative(Lc::n("PageTop"));
|
||||
///
|
||||
/// let photo = Image::with(image::Source::responsive("/files/photo.jpg"))
|
||||
/// .with_size(image::Size::Width(UnitValue::Px(320)))
|
||||
/// .with_alternative(Lc::n("Team photo"));
|
||||
/// ```
|
||||
#[derive(AutoDefault, Clone, Debug, Getters)]
|
||||
pub struct Image {
|
||||
/// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente.
|
||||
props: Props,
|
||||
/// Devuelve las dimensiones de la imagen.
|
||||
size: image::Size,
|
||||
/// Devuelve el origen de la imagen.
|
||||
source: image::Source,
|
||||
/// Devuelve el texto alternativo localizado.
|
||||
alternative: Attr<Lc>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Component for Image {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<String> {
|
||||
self.props.get_id()
|
||||
}
|
||||
|
||||
fn setup(&mut self, _cx: &Context) {
|
||||
self.alter_prop(PropsOp::prepend_classes(match self.source() {
|
||||
image::Source::Logo(_) => "image image-fluid",
|
||||
image::Source::Responsive(_) => "image image-fluid",
|
||||
image::Source::Thumbnail(_) => "image image-thumbnail",
|
||||
image::Source::Plain(_) => "image",
|
||||
}));
|
||||
// El tamaño se aplica como declaraciones `style` individuales sobre `Props`.
|
||||
match *self.size() {
|
||||
image::Size::Auto => {}
|
||||
image::Size::Dimensions(w, h) => {
|
||||
self.alter_prop(PropsOp::add_style("width", w.to_string()));
|
||||
self.alter_prop(PropsOp::add_style("height", h.to_string()));
|
||||
}
|
||||
image::Size::Width(w) => {
|
||||
self.alter_prop(PropsOp::add_style("width", w.to_string()));
|
||||
}
|
||||
image::Size::Height(h) => {
|
||||
self.alter_prop(PropsOp::add_style("height", h.to_string()));
|
||||
}
|
||||
image::Size::Both(v) => {
|
||||
self.alter_prop(PropsOp::add_style("width", v.to_string()));
|
||||
self.alter_prop(PropsOp::add_style("height", v.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn prepare(&self, cx: &mut Context) -> Result<Markup, ComponentError> {
|
||||
let alt_text = self.alternative().lookup(cx).unwrap_or_default();
|
||||
let source = match self.source() {
|
||||
image::Source::Logo(logo) => {
|
||||
let is_decorative = alt_text.is_empty();
|
||||
return Ok(html! {
|
||||
span
|
||||
(self.props())
|
||||
role=[(!is_decorative).then_some("img")]
|
||||
aria-label=[(!is_decorative).then_some(alt_text)]
|
||||
aria-hidden=[is_decorative.then_some("true")]
|
||||
{
|
||||
(logo.markup(cx))
|
||||
}
|
||||
});
|
||||
}
|
||||
image::Source::Responsive(source) => Some(source),
|
||||
image::Source::Thumbnail(source) => Some(source),
|
||||
image::Source::Plain(source) => Some(source),
|
||||
};
|
||||
Ok(html! {
|
||||
img
|
||||
src=[source]
|
||||
alt=(alt_text)
|
||||
(self.props()) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Image {
|
||||
/// Crea rápidamente una imagen especificando su origen.
|
||||
pub fn with(source: image::Source) -> Self {
|
||||
Self::default().with_source(source)
|
||||
}
|
||||
|
||||
// **< Image BUILDER >**************************************************************************
|
||||
|
||||
/// Establece el identificador único del componente; igual a `with_prop(PropsOp::set_id(id))`.
|
||||
#[builder_fn]
|
||||
pub fn with_id(mut self, id: impl Into<CowStr>) -> Self {
|
||||
self.props.alter_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// Modifica identificador, clases CSS, atributos HTML o valores extra del componente.
|
||||
#[builder_fn]
|
||||
pub fn with_prop(mut self, op: PropsOp) -> Self {
|
||||
self.props.alter_prop(op);
|
||||
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 un *texto localizado* ([`Lc`]) alternativo 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: Lc) -> Self {
|
||||
self.alternative.alter_value(alt);
|
||||
self
|
||||
}
|
||||
}
|
||||
84
src/base/component/image/props.rs
Normal file
84
src/base/component/image/props.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
// **< Size >***************************************************************************************
|
||||
|
||||
/// Define las **dimensiones** de una imagen ([`Image`](super::Image)).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, PartialEq)]
|
||||
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`](super::Image)).
|
||||
///
|
||||
/// Las variantes son puramente semánticas. El componente aplica una clase CSS base según la
|
||||
/// variante en su propio `setup()`; los temas pueden sobrescribirla interceptando el renderizado
|
||||
/// del componente.
|
||||
#[derive(AutoDefault, Clone, Debug, PartialEq)]
|
||||
pub enum Source {
|
||||
/// Imagen con el logotipo de PageTop.
|
||||
#[default]
|
||||
Logo(PageTopSvg),
|
||||
/// Imagen que se adapta automáticamente a su contenedor.
|
||||
///
|
||||
/// Lleva asociada la URL (o ruta) de la imagen.
|
||||
Responsive(CowStr),
|
||||
/// Imagen que aplica un estilo de miniatura.
|
||||
///
|
||||
/// Lleva asociada la URL (o ruta) de la imagen.
|
||||
Thumbnail(CowStr),
|
||||
/// Imagen sin modificadores adicionales de estilo, útil para controlar la apariencia con CSS
|
||||
/// propio.
|
||||
///
|
||||
/// Lleva asociada la URL (o ruta) de la imagen.
|
||||
Plain(CowStr),
|
||||
}
|
||||
|
||||
impl Source {
|
||||
/// Imagen con el logotipo de PageTop.
|
||||
#[inline]
|
||||
pub fn logo(svg: PageTopSvg) -> Self {
|
||||
Self::Logo(svg)
|
||||
}
|
||||
|
||||
/// Imagen responsive.
|
||||
#[inline]
|
||||
pub fn responsive(url: impl Into<CowStr>) -> Self {
|
||||
Self::Responsive(url.into())
|
||||
}
|
||||
|
||||
/// Imagen miniatura.
|
||||
#[inline]
|
||||
pub fn thumbnail(url: impl Into<CowStr>) -> Self {
|
||||
Self::Thumbnail(url.into())
|
||||
}
|
||||
|
||||
/// Imagen sin modificadores adicionales de estilo.
|
||||
#[inline]
|
||||
pub fn plain(url: impl Into<CowStr>) -> Self {
|
||||
Self::Plain(url.into())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue