count_badge.rs

 1use gpui::FontWeight;
 2
 3use crate::prelude::*;
 4
 5/// A small, pill-shaped badge that displays a numeric count.
 6///
 7/// The count is capped at 99 and displayed as "99+" beyond that.
 8#[derive(IntoElement, RegisterComponent)]
 9pub struct CountBadge {
10    count: usize,
11}
12
13impl CountBadge {
14    pub fn new(count: usize) -> Self {
15        Self { count }
16    }
17}
18
19impl RenderOnce for CountBadge {
20    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
21        let label = if self.count > 99 {
22            "99+".to_string()
23        } else {
24            self.count.to_string()
25        };
26
27        let bg = cx
28            .theme()
29            .colors()
30            .editor_background
31            .blend(cx.theme().status().error.opacity(0.4));
32
33        h_flex()
34            .absolute()
35            .top_0()
36            .right_0()
37            .p_px()
38            .h_3p5()
39            .min_w_3p5()
40            .rounded_full()
41            .justify_center()
42            .text_center()
43            .border_1()
44            .border_color(cx.theme().colors().border)
45            .bg(bg)
46            .shadow_sm()
47            .child(
48                Label::new(label)
49                    .size(LabelSize::Custom(rems_from_px(9.)))
50                    .weight(FontWeight::MEDIUM),
51            )
52    }
53}
54
55impl Component for CountBadge {
56    fn scope() -> ComponentScope {
57        ComponentScope::Status
58    }
59
60    fn description() -> Option<&'static str> {
61        Some("A small, pill-shaped badge that displays a numeric count.")
62    }
63
64    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
65        let container = || {
66            div()
67                .relative()
68                .size_8()
69                .border_1()
70                .border_color(cx.theme().colors().border)
71                .bg(cx.theme().colors().background)
72        };
73
74        Some(
75            v_flex()
76                .gap_6()
77                .child(example_group_with_title(
78                    "Count Badge",
79                    vec![
80                        single_example(
81                            "Basic Count",
82                            container().child(CountBadge::new(3)).into_any_element(),
83                        ),
84                        single_example(
85                            "Capped Count",
86                            container().child(CountBadge::new(150)).into_any_element(),
87                        ),
88                    ],
89                ))
90                .into_any_element(),
91        )
92    }
93}