Skip to main content

yew_nav_link/components/
badge.rs

1// SPDX-FileCopyrightText: 2024-2026 RAprogramm <andrey.rozanov-vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4//! # `NavBadge`
5//!
6//! Small colored label for showing counts, statuses, or labels inside
7//! navigation links. Renders a `<span>` you can place anywhere inside
8//! a `NavLink` or `NavItem`.
9//!
10//! # Quick Start
11//!
12//! ```rust
13//! use yew::prelude::*;
14//! use yew_nav_link::{NavBadge, NavItem, NavLink, NavList};
15//! use yew_router::prelude::*;
16//!
17//! # #[derive(Clone, PartialEq, Routable)]
18//! # enum Route {
19//! #     #[at("/")]
20//! #     Home,
21//! # }
22//! #[component]
23//! fn Nav() -> Html {
24//!     html! {
25//!         <NavList>
26//!             <NavItem>
27//!                 <NavLink<Route> to={Route::Home}>
28//!                     { "Messages " }
29//!                     <NavBadge variant="danger">{ "3" }</NavBadge>
30//!                 </NavLink<Route>>
31//!             </NavItem>
32//!         </NavList>
33//!     }
34//! }
35//! ```
36//!
37//! Available variants: `"primary"`, `"success"`, `"warning"`, `"danger"`.
38//! Set `pill=true` for fully rounded corners.
39//!
40//! # CSS Classes
41//!
42//! | Class | When Applied |
43//! |-------|--------------|
44//! | `nav-badge` | Always |
45//! | `nav-badge-pill` | When `pill` is `true` |
46//! | `nav-badge-{variant}` | Based on the `variant` prop |
47//!
48//! # Props
49//!
50//! | Prop | Type | Default | Description |
51//! |------|------|---------|-------------|
52//! | `variant` | `&'static str` | `"primary"` | Color variant |
53//! | `pill` | `bool` | `false` | Rounded pill shape |
54//! | `classes` | `Classes` | — | Additional CSS classes |
55//! | `children` | `Children` | — | Badge content |
56
57use yew::prelude::*;
58
59/// Properties for the [`NavBadge`] component.
60///
61/// | Prop | Type | Default | Description |
62/// |------|------|---------|-------------|
63/// | `variant` | `&'static str` | `"primary"` | Visual variant name |
64/// | `pill` | `bool` | `false` | Pill-shaped corners |
65/// | `classes` | `Classes` | — | Additional CSS classes |
66/// | `children` | `Children` | — | Badge content |
67#[derive(Properties, Clone, PartialEq, Debug)]
68pub struct NavBadgeProps {
69    /// Additional CSS classes applied to the badge.
70    #[prop_or_default]
71    pub classes: Classes,
72
73    /// Visual variant name, e.g. `"primary"`, `"success"`, `"danger"`.
74    #[prop_or("primary")]
75    pub variant: &'static str,
76
77    /// Render the badge with pill-shaped (fully rounded) corners.
78    #[prop_or_default]
79    pub pill: bool,
80
81    /// Content rendered inside the badge.
82    #[prop_or_default]
83    pub children: Children
84}
85
86/// Badge component for displaying status or count indicators on navigation
87/// items.
88///
89/// # CSS Classes
90///
91/// - `nav-badge` - Always applied
92/// - `nav-badge-pill` - Applied when `pill` is `true`
93/// - `nav-badge-{variant}` - Applied based on the `variant` prop
94#[function_component]
95pub fn NavBadge(props: &NavBadgeProps) -> Html {
96    let mut classes = props.classes.clone();
97    classes.push("nav-badge");
98
99    if props.pill {
100        classes.push("nav-badge-pill");
101    }
102
103    let variant_class = format!("nav-badge-{}", props.variant);
104    classes.push(variant_class);
105
106    html! {
107        <span {classes}>
108            { for props.children.iter() }
109        </span>
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn nav_badge_props_default() {
119        let props = NavBadgeProps {
120            classes:  Classes::default(),
121            variant:  "primary",
122            pill:     false,
123            children: Children::new(vec![])
124        };
125
126        assert!(!props.pill);
127        assert_eq!(props.variant, "primary");
128    }
129
130    #[test]
131    fn nav_badge_clone() {
132        let props1 = NavBadgeProps {
133            classes:  Classes::from("test"),
134            variant:  "success",
135            pill:     true,
136            children: Children::new(vec![])
137        };
138
139        let props2 = props1.clone();
140        assert_eq!(props1.variant, props2.variant);
141        assert_eq!(props1.pill, props2.pill);
142    }
143}