radix_leptos_primitives/components/
badge.rs1use leptos::prelude::*;
2use crate::utils::{merge_classes, generate_id};
3
4#[derive(Clone, Copy, PartialEq, Eq, Hash)]
6pub enum BadgeVariant {
7 Default,
8 Primary,
9 Secondary,
10 Success,
11 Error,
12 Warning,
13 Info,
14 Outline,
15}
16
17#[derive(Clone, Copy, PartialEq, Eq, Hash)]
19pub enum BadgeSize {
20 Small,
21 Medium,
22 Large,
23}
24
25
26#[component]
28pub fn Badge(
29 #[prop(optional, default = BadgeVariant::Default)]
31 variant: BadgeVariant,
32 #[prop(optional, default = BadgeSize::Medium)]
34 size: BadgeSize,
35 #[prop(optional, default = false)]
37 interactive: bool,
38 #[prop(optional, default = false)]
40 disabled: bool,
41 #[prop(optional)]
43 class: Option<String>,
44 #[prop(optional)]
46 on_click: Option<Callback<web_sys::MouseEvent>>,
47 children: Children,
49) -> impl IntoView {
50 let variant_class = move || match variant {
51 BadgeVariant::Default => "radix-badge--variant-default",
52 BadgeVariant::Primary => "radix-badge--variant-primary",
53 BadgeVariant::Secondary => "radix-badge--variant-secondary",
54 BadgeVariant::Success => "radix-badge--variant-success",
55 BadgeVariant::Error => "radix-badge--variant-error",
56 BadgeVariant::Warning => "radix-badge--variant-warning",
57 BadgeVariant::Info => "radix-badge--variant-info",
58 BadgeVariant::Outline => "radix-badge--variant-outline",
59 };
60
61 let size_class = move || match size {
62 BadgeSize::Small => "radix-badge--size-small",
63 BadgeSize::Medium => "radix-badge--size-medium",
64 BadgeSize::Large => "radix-badge--size-large",
65 };
66
67 let handle_click = move |e: web_sys::MouseEvent| {
68 if !disabled && interactive {
69 if let Some(callback) = on_click {
70 callback.run(e);
71 }
72 }
73 };
74
75 let class_value = class.unwrap_or_default();
76 let children_view = children();
77
78 let mut base_classes = vec!["radix-badge", &variant_class(), &size_class(), &class_value];
79
80 if interactive && !disabled {
81 base_classes.push("radix-badge--interactive");
82 }
83
84 let final_classes = base_classes;
85
86 view! {
87 <span
88 class=merge_classes(final_classes)
89 role="status"
90 on:click=handle_click
91 >
92 {children_view}
93 </span>
94 }
95}
96
97#[component]
99pub fn BadgeCount(
100 count: u32,
102 #[prop(optional, default = 99)]
104 max_count: u32,
105 #[prop(optional, default = BadgeVariant::Error)]
107 variant: BadgeVariant,
108 #[prop(optional, default = BadgeSize::Small)]
110 size: BadgeSize,
111 #[prop(optional, default = false)]
113 show_zero: bool,
114 #[prop(optional)]
116 class: Option<String>,
117) -> impl IntoView {
118 let display_count = move || {
119 if count > max_count {
120 format!("{}+", max_count)
121 } else {
122 count.to_string()
123 }
124 };
125
126 let should_show = move || show_zero || count > 0;
127
128 let class_value = class.unwrap_or_default();
129
130 view! {
131 {move || {
132 if should_show() {
133 view! {
134 <Badge
135 variant=variant
136 size=size
137 class=class_value.clone()
138 >
139 <span class="radix-badge-count-text">
140 {display_count()}
141 </span>
142 </Badge>
143 }.into_any()
144 } else {
145 let _: () = view! { <></> };
146 ().into_any()
147 }
148 }}
149 }
150}
151
152#[component]
154pub fn BadgeDot(
155 #[prop(optional, default = BadgeVariant::Success)]
157 variant: BadgeVariant,
158 #[prop(optional, default = BadgeSize::Small)]
160 size: BadgeSize,
161 #[prop(optional, default = false)]
163 pulsing: bool,
164 #[prop(optional)]
166 class: Option<String>,
167) -> impl IntoView {
168 let class_value = class.unwrap_or_default();
169 let variant_class = move || match variant {
170 BadgeVariant::Default => "radix-badge-dot--variant-default",
171 BadgeVariant::Primary => "radix-badge-dot--variant-primary",
172 BadgeVariant::Secondary => "radix-badge-dot--variant-secondary",
173 BadgeVariant::Success => "radix-badge-dot--variant-success",
174 BadgeVariant::Error => "radix-badge-dot--variant-error",
175 BadgeVariant::Warning => "radix-badge-dot--variant-warning",
176 BadgeVariant::Info => "radix-badge-dot--variant-info",
177 BadgeVariant::Outline => "radix-badge-dot--variant-outline",
178 };
179
180 let size_class = move || match size {
181 BadgeSize::Small => "radix-badge-dot--size-small",
182 BadgeSize::Medium => "radix-badge-dot--size-medium",
183 BadgeSize::Large => "radix-badge-dot--size-large",
184 };
185
186 let pulsing_class = move || {
187 if pulsing {
188 "radix-badge-dot--pulsing"
189 } else {
190 ""
191 }
192 };
193
194 view! {
195 <span
196 class=merge_classes(vec![
197 "radix-badge-dot",
198 variant_class(),
199 size_class(),
200 pulsing_class(),
201 &class_value,
202 ])
203 role="status"
204 >
205 <span class="radix-badge-dot-inner"></span>
206 </span>
207 }
208}