From 4cc72aac0e0abb8bb06ef16d3685c3ca2ad454de Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Sat, 1 Aug 2026 12:15:00 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(base):=20A=C3=B1ade=20componente?= =?UTF-8?q?=20`Badge`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incluye soporte específico en Bootsier (`BadgeBootsier`) para fijar el color de fondo con contraste de texto garantizado. --- extensions/pagetop-bootsier/src/theme/bs.rs | 4 ++ .../pagetop-bootsier/src/theme/bs/badge.rs | 47 +++++++++++++ src/base/component.rs | 3 + src/base/component/badge.rs | 66 +++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 extensions/pagetop-bootsier/src/theme/bs/badge.rs create mode 100644 src/base/component/badge.rs diff --git a/extensions/pagetop-bootsier/src/theme/bs.rs b/extensions/pagetop-bootsier/src/theme/bs.rs index ae1657fd..2070aeb2 100644 --- a/extensions/pagetop-bootsier/src/theme/bs.rs +++ b/extensions/pagetop-bootsier/src/theme/bs.rs @@ -1,5 +1,9 @@ //! Componentes proporcionados por el tema. +// Badge. +pub(crate) mod badge; +pub use badge::{Badge, BadgeBootsier}; + // Button. mod button; pub use button::{Button, ButtonAction}; diff --git a/extensions/pagetop-bootsier/src/theme/bs/badge.rs b/extensions/pagetop-bootsier/src/theme/bs/badge.rs new file mode 100644 index 00000000..95f4ef50 --- /dev/null +++ b/extensions/pagetop-bootsier/src/theme/bs/badge.rs @@ -0,0 +1,47 @@ +use pagetop::prelude::*; + +use crate::theme::*; + +pub use pagetop::base::component::Badge; + +const EXTRA_TEXT_BG: &str = "bootsier.badge.text_bg"; + +/// Extensión de Bootsier para [`Badge`]. +/// +/// Proporciona el método [`with_text_bg()`](Self::with_text_bg) para fijar el color de fondo del +/// badge usando un color de texto con contraste suficiente garantizado. +/// +/// ```rust,no_run +/// use pagetop::prelude::*; +/// use pagetop_bootsier::theme::*; +/// +/// let admin = bs::Badge::new() +/// .with_label(L10n::n("Admin")) +/// .with_text_bg(ThemeColor::Danger); +/// ``` +pub trait BadgeBootsier { + #[builder_fn] + fn with_text_bg(self, color: ThemeColor) -> Self; +} + +impl BadgeBootsier for Badge { + /// Establece el color de fondo usando un color de texto con contraste garantizado. + /// + /// Igual a `with_prop(PropsOp::add_classes(class::TextColor::Bg(color)))`, pero sin el riesgo + /// de acumular más de una clase `text-bg-{color}` si se llama varias veces. + #[builder_fn] + fn with_text_bg(mut self, color: ThemeColor) -> Self { + self.alter_prop(PropsOp::set_extra(EXTRA_TEXT_BG, color)); + self + } +} + +// **< Badge SETUP >******************************************************************************** + +pub(crate) fn setup(badge: &mut Badge) { + let color = badge.props().extra_or(EXTRA_TEXT_BG, ThemeColor::Secondary); + badge.alter_prop(PropsOp::replace_classes( + "badge", + util::join!("badge ", class::TextColor::Bg(color).to_class()), + )); +} diff --git a/src/base/component.rs b/src/base/component.rs index fe3c734c..5a5dc8d1 100644 --- a/src/base/component.rs +++ b/src/base/component.rs @@ -2,6 +2,9 @@ pub mod layout; +mod badge; +pub use badge::Badge; + mod block; pub use block::Block; diff --git a/src/base/component/badge.rs b/src/base/component/badge.rs new file mode 100644 index 00000000..eb4bf173 --- /dev/null +++ b/src/base/component/badge.rs @@ -0,0 +1,66 @@ +use crate::prelude::*; + +/// Componente para mostrar una **etiqueta corta informativa** (*badge*). +/// +/// # Ejemplo +/// +/// ```rust,no_run +/// use pagetop::prelude::*; +/// +/// let badge = Badge::new().with_label(L10n::n("Admin")); +/// ``` +#[derive(AutoDefault, Clone, Debug, Getters)] +pub struct Badge { + /// Devuelve identificador, clases CSS, atributos HTML y valores extra del componente. + props: Props, + /// Devuelve la etiqueta del badge. + label: L10n, +} + +#[async_trait] +impl Component for Badge { + fn new() -> Self { + Self::default() + } + + fn id(&self) -> Option { + self.props.get_id() + } + + fn setup(&mut self, _cx: &Context) { + self.alter_prop(PropsOp::prepend_classes("badge")); + } + + async fn prepare(&self, cx: &mut Context) -> Result { + Ok(html! { + span (self.props()) { + (self.label().using(cx)) + } + }) + } +} + +impl Badge { + // **< Badge 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) -> Self { + self.props.alter_id(id); + self + } + + /// Modifica identificador, clases CSS o atributos HTML del componente. + #[builder_fn] + pub fn with_prop(mut self, op: PropsOp) -> Self { + self.props.alter_prop(op); + self + } + + /// Establece la etiqueta del badge. + #[builder_fn] + pub fn with_label(mut self, label: L10n) -> Self { + self.label = label; + self + } +}