use dioxus::prelude::*;
use crate::types::{BadgeFill, Color};
#[derive(Clone, PartialEq, Props)]
pub struct BadgeProps {
#[props(default = Color::Primary)]
pub color: Color,
#[props(default)]
pub fill: BadgeFill,
#[props(default)]
pub pill: bool,
#[props(default)]
pub class: String,
#[props(default)]
pub onclick: Option<EventHandler<MouseEvent>>,
#[props(default)]
pub href: Option<String>,
#[props(default)]
pub target: Option<String>,
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
pub children: Element,
}
fn badge_element(has_href: bool) -> &'static str {
if has_href { "a" } else { "span" }
}
fn badge_class(color: Color, fill: BadgeFill, pill: bool, class: &str) -> String {
let color_classes = match fill {
BadgeFill::TextBg => format!(" text-bg-{color}"),
BadgeFill::Bg => format!(" bg-{color}"),
BadgeFill::Subtle => format!(" bg-{color}-subtle text-{color}-emphasis"),
BadgeFill::None => String::new(),
};
let pill = if pill { " rounded-pill" } else { "" };
if class.is_empty() {
format!("badge{color_classes}{pill}")
} else {
format!("badge{color_classes}{pill} {class}")
}
}
#[component]
pub fn Badge(props: BadgeProps) -> Element {
let full_class = badge_class(props.color, props.fill, props.pill, &props.class);
if badge_element(props.href.is_some()) == "a" {
let href = props.href.clone().expect("anchor form implies an href");
let target = props.target.clone();
return rsx! {
a {
class: "{full_class}",
href: "{href}",
target: target,
onclick: move |evt| {
if let Some(handler) = &props.onclick {
handler.call(evt);
}
},
..props.attributes,
{props.children}
}
};
}
rsx! {
span {
class: "{full_class}",
onclick: move |evt| {
if let Some(handler) = &props.onclick {
handler.call(evt);
}
},
..props.attributes,
{props.children}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn badge_is_a_span_without_an_href() {
assert_eq!(badge_element(false), "span");
}
#[test]
fn badge_with_an_href_is_an_anchor() {
assert_eq!(badge_element(true), "a");
}
#[test]
fn badge_default_fill_is_text_bg() {
assert_eq!(
badge_class(Color::Primary, BadgeFill::default(), false, ""),
"badge text-bg-primary"
);
}
#[test]
fn badge_bg_fill_omits_the_foreground() {
assert_eq!(
badge_class(Color::Secondary, BadgeFill::Bg, false, ""),
"badge bg-secondary"
);
}
#[test]
fn badge_subtle_fill_emits_both_halves_of_the_pair() {
assert_eq!(
badge_class(Color::Info, BadgeFill::Subtle, false, ""),
"badge bg-info-subtle text-info-emphasis"
);
}
#[test]
fn badge_none_fill_emits_geometry_only() {
assert_eq!(
badge_class(Color::Primary, BadgeFill::None, false, ""),
"badge"
);
}
#[test]
fn badge_pill_and_extra_classes_survive_every_fill() {
assert_eq!(
badge_class(Color::Danger, BadgeFill::TextBg, true, "ms-2"),
"badge text-bg-danger rounded-pill ms-2"
);
assert_eq!(
badge_class(Color::Danger, BadgeFill::None, true, "ms-2"),
"badge rounded-pill ms-2"
);
}
}